library(tidyverse)
library(ggpubr)
library(survival)
library(survminer)
library(patchwork)
bulk2046 <- read_csv('BALL2046_DevState_Updated_April2024_Fusions_MRD_AZ.csv')

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  Age = col_double(),
  WBC = col_double(),
  oscensor = col_double(),
  ostime = col_double(),
  efscensor = col_double(),
  efstime = col_double(),
  HSC_MPP = col_double(),
  Myeloid_Prog = col_double(),
  Pre_pDC = col_double(),
  Early_Lymphoid = col_double(),
  Pro_B = col_double(),
  Pre_B = col_double(),
  Mature_B = col_double(),
  T_NK = col_double(),
  Monocyte = col_double(),
  Erythroid = col_double()
)
ℹ Use `spec()` for the full column specifications.
bulk2046 

PCA on Dev State Abundance

Include the four main lineages along B cell development

LineageScores <- bulk2046 %>% select(PatientID, HSC_MPP, Early_Lymphoid, Pro_B, Pre_B) %>%   
  column_to_rownames('PatientID') %>% data.matrix()
bulk2046$PC1 <- prcomp(LineageScores)[5]$x[,1]
prcomp(LineageScores, scale=T, center=T)
Standard deviations (1, .., p=4):
[1] 1.2839839 1.1670773 0.8355012 0.5396791

Rotation (n x k) = (4 x 4):
                      PC1        PC2        PC3        PC4
HSC_MPP         0.5700131 -0.4172374  0.4470556 -0.5487615
Early_Lymphoid  0.5125228 -0.4916068 -0.4791663  0.5157933
Pro_B          -0.4753292 -0.5306387 -0.4971588 -0.4952958
Pre_B          -0.4318188 -0.5501439  0.5686599  0.4330129

Principal Component 1 is a Multipotency Score

DevState_PCA <- data.frame(prcomp(LineageScores, scale=T, center=T)[2]$rotation) %>% rownames_to_column('DevState')
color <- ifelse(DevState_PCA$PC1 > 0, 'darkgreen', 'darkorange')


DevState_PCA %>% 
  mutate(DevState = factor(DevState %>% str_replace('HSC_MPP', 'HSC/MPP') %>% str_replace('_B','-B') %>% str_replace('_',' '), 
                           levels = rev(c("HSC/MPP", "Early Lymphoid", "Pro-B", "Pre-B")))) %>% 
  ggplot(aes(x = DevState, y = PC1)) +
  geom_bar(stat = "identity", show.legend = FALSE, fill = color, color = "white") +
  geom_hline(yintercept = 0, color = 1, lwd = 0.2) +
  geom_text(aes(label = DevState, # Text with groups
                hjust = ifelse(PC1 < 0, 1.25, -0.15),
                vjust = 0.5), size = 3.5) +
  xlab("Developmental State") + ylab("PC1 Feature Loadings") +
  scale_y_continuous(breaks = seq(-1, 1, by = 0.25), limits = c(-0.8, 0.8)) +
  coord_flip() +
  theme_minimal() +
  theme(axis.text.y = element_blank(),  # Remove Y-axis texts
        axis.ticks.y = element_blank(), # Remove Y-axis ticks
        panel.grid.major.y = element_blank(),
        panel.grid.minor.x = element_blank()) # Remove horizontal grid

ggsave('BALL_MultipotencyScore_Figures/MultipotencyScore_PC1_FeatureLoadings.pdf', height = 3.2, width=6)

Derive gene signature for estimating PC1

train_LASSO <- function(x_train, y_train, alpha = 1){
  
  train_y <- y_train$PC1
  
  # Perform Lasso regression with LOOCV 
  model <- cv.glmnet(x = x_train, y = train_y, nfold = 10, family = 'gaussian', alpha = alpha, maxit=1000000, standardize=FALSE)
  #plot(model)

  return(model)
}

evaluate_model <- function(model, x_val, anno_val, lambda, 
                           feature_name, iteration, foldname){

  # Create score classification with survival and get covariates
  pred_y <- predict(model, x_val, s = lambda) %>% data.frame()
  colnames(pred_y) <- 'PredScore'
  pred_y <- pred_y %>% rownames_to_column('Patient') %>% 
    # add anno to get covariates
    left_join(anno_val, by = 'Patient')
  
  # Calculate correlation in validation set
  pearson <- cor(pred_y$PredScore, pred_y$PC1, method = 'pearson')
  spearman <- cor(pred_y$PredScore, pred_y$PC1, method = 'spearman')
  
  # Summary Metrics
  summary_metrics <- data.frame(
    'model_id' = paste0(feature_name, '_iter', iteration, '_', foldname),
    'lambda' = lambda,
    'model_size' = sum(coef(model, s = lambda)!=0),
    'pearson' = pearson,
    'spearman' = spearman,
    'features' = feature_name,
    'iteration' = iteration,
    'foldname' = foldname
  )
  return(summary_metrics)
}


gridsearch_lasso <- function(expr_train, expr_val, anno_train, anno_val, features, feature_name,
                             iteration, foldname, summary_metrics){
  
  # Filter expr matrix for feature set
  x_train <- expr_train[, colnames(expr_train) %in% features]
  x_val <- expr_val[, colnames(expr_val) %in% features]

  # Train LASSO 
  model <- train_LASSO(x_train, anno_train)

  # Get summary metrics for lambda.min and lambda.1se
  for(lambda in c('lambda.min', 'lambda.1se')){
    summary_metrics <- summary_metrics %>% rbind(
      evaluate_model(model = model, x_val = x_val, anno_val = anno_val, lambda = lambda, 
                     feature_name = feature_name, iteration = iteration, foldname = foldname))
  }
  
  return(summary_metrics)
}


nestedCV_regression <- function(train_anno, train_expr, iteration, feature_sets, summary_metrics){
  # set up random seed and shuffle data 
  set.seed(iteration)
  train_anno <- train_anno[sample(nrow(train_anno)),]
  train_expr <- train_expr[sample(nrow(train_expr)),]
  
  ## 10-fold outer cross validation
  folds <- rsample::vfold_cv(train_anno, 10)
  for(outer_cv in 1:10){
    # fold ID
    foldname <- folds$id[[outer_cv]]
    # get anno splits
    anno_train <- analysis(folds$splits[[outer_cv]])
    anno_val <- assessment(folds$splits[[outer_cv]])
    # get expr splits
    expr_train <- train_expr[anno_train$Patient,]
    expr_val <- train_expr[anno_val$Patient,]
    
    # Iterate through feature set and run gridsearch to train survival functions
    for(feature_name in names(feature_sets)){
      # get feature list
      features <- feature_sets[[feature_name]]
      # run gridsearch and get results
      summary_metrics <- gridsearch_lasso(expr_train = expr_train, expr_val = expr_val, anno_train = anno_train, anno_val = anno_val, 
                                  features = features, feature_name = feature_name, iteration = iteration, foldname = foldname,
                                  summary_metrics = summary_metrics)
    }
  }
  return(summary_metrics)
}
bulk2046_vst <- readRDS('../BALL2046_BulkRNA_vst.rds')
bulk2046_vst %>% dim()
[1] 35289  2046
# Train from genes used to predict the four bdev lineage populations
modelweights <- read_csv("../NMF_Lasso_ModelWeights.csv") %>% select(Gene, HSC_MPP, Early_Lymphoid, Pro_B, Pre_B) %>% 
  rowwise() %>% mutate(sumweights = sum(HSC_MPP, Early_Lymphoid, Pro_B, Pre_B)) %>% filter(sumweights != 0) %>% select(-sumweights) 

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  Gene = col_character(),
  HSC_MPP = col_double(),
  Myeloid_Prog = col_double(),
  Pre_pDC = col_double(),
  Early_Lymphoid = col_double(),
  Pro_B = col_double(),
  Pre_B = col_double(),
  Mature_B = col_double(),
  Erythroid = col_double(),
  Monocyte = col_double(),
  T_NK = col_double()
)
bulk2046_vst_filtered <- bulk2046_vst[modelweights$Gene,] 
bulk2046_vst_filtered %>% dim()
[1]  115 2046
library(tidymodels)
library(glmnet)

CVoutput <- data.frame()
train_x <- bulk2046_vst_filtered[,bulk2046$SampleID_old] %>% t()
train_y <- bulk2046 %>% select(Patient = SampleID_old, PC1)# %>% mutate(PC1 = -PC1)
featurespace <- list('ModelWeights115' = modelweights$Gene)
temp_output <- data.frame()


for(iteration in 1:10){
  print(paste0('iteration ', iteration))
  CVoutput <- nestedCV_regression(train_anno = train_y, train_expr = train_x, iteration = iteration, feature_sets = featurespace, 
                                summary_metrics = CVoutput) 
}
[1] "iteration 1"
[1] "iteration 2"
[1] "iteration 3"
[1] "iteration 4"
[1] "iteration 5"
[1] "iteration 6"
[1] "iteration 7"
[1] "iteration 8"
[1] "iteration 9"
[1] "iteration 10"
## annotate and add to final output
CVoutput 
CVoutput %>% write_csv('RepNestedCV_results_PC1multipotency_Regression.csv')
train_LASSO <- function(x_train, y_train, alpha = 1){
  
  train_y <- y_train$PC1
  
  # Perform Lasso regression with LOOCV 
  model <- cv.glmnet(x = x_train, y = train_y, nfold = 10, family = 'gaussian', alpha = alpha, maxit=1000000, standardize=FALSE)
  #plot(model)

  return(model)
}
model <- train_LASSO(train_x, y_train = train_y)

# PC1 model weights
PC1_modelweights <- data.frame()

PC1_modelweights <- model %>% coef(s = 'lambda.1se') %>% data.matrix() %>% 
      data.frame() %>% dplyr::rename(Weight = s1) %>% rownames_to_column('Gene') %>% 
      tail(-1) %>% filter(Weight != 0) %>% arrange(-Weight) 
    
PC1_modelweights <- PC1_modelweights %>% select(Gene, Weight)
PC1_modelweights
# Create final model matrix
modelweights_withMultipotency <- read_csv("../NMF_Lasso_ModelWeights.csv")  %>% 
  left_join(PC1_modelweights %>% select(Gene, Multipotency_Score = Weight)) %>% 
  replace(is.na(.), 0) 

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  Gene = col_character(),
  HSC_MPP = col_double(),
  Myeloid_Prog = col_double(),
  Pre_pDC = col_double(),
  Early_Lymphoid = col_double(),
  Pro_B = col_double(),
  Pre_B = col_double(),
  Mature_B = col_double(),
  Erythroid = col_double(),
  Monocyte = col_double(),
  T_NK = col_double()
)
Joining with `by = join_by(Gene)`
modelweights_withMultipotency %>% write_csv("DevState_Lasso_ModelWeights_withMultipotencyScore_May2024.csv")
modelweights_withMultipotency
calculate_DevState_scores = function(query, modelweights, scale = TRUE, sampleID = 'Patient'){
  
  # Check for overlap with model genes and query genes
  querygenes <- rownames(query)
  modelweights_missing <- sum(!(modelweights$Gene %in% querygenes))
  # check for missing genes
  if(modelweights_missing > 0){
    print(paste0('Warning: ', modelweights_missing, ' genes from Dev State models are missing from query dataset'))
  }
  
  # filter model weights
  modelweights <- modelweights %>% filter(Gene %in% querygenes)
  modelweights_mat <- modelweights %>% column_to_rownames('Gene') %>% data.matrix()
  
  # multiply query by Dev State lasso weights
  scored <- (t(query[modelweights$Gene,]) %*% modelweights_mat) %>% data.matrix() 
  if(scale == TRUE){
    scored <- scale(scored)
  }
  scored <- scored %>% as.data.frame() %>% rownames_to_column(sampleID) 
  
  return(scored)
}

Calculate in bulk2046 and validate

bulk2046 <- bulk2046 %>% 
  left_join( calculate_DevState_scores(bulk2046_vst, modelweights_withMultipotency, scale = T, sampleID = 'SampleID_old') %>% select(SampleID_old, Multipotency_Score) ) 
Joining with `by = join_by(SampleID_old)`
bulk2046
# Load repeated nested cross-validation (10-fold, 10 repeats) results 
CVoutput <- read_csv('RepNestedCV_results_PC1multipotency_Regression.csv')

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  model_id = col_character(),
  lambda = col_character(),
  model_size = col_double(),
  pearson = col_double(),
  spearman = col_double(),
  features = col_character(),
  iteration = col_double(),
  foldname = col_character()
)
PC1model_CVplot <- CVoutput %>% filter(lambda == 'lambda.1se') %>%
  mutate(model = '') %>% 
  ggplot(aes(x = model, y = pearson^2)) +
  ylab('Pearson Correlation with PC1') + xlab('PC1 Models\n(Cross-Validation)') + 
  geom_boxplot(outlier.size=0) + ggbeeswarm::geom_quasirandom(width=0.32) + theme_pubr() + theme(axis.ticks.x = element_blank())

# Get correlation across full cohort
bulk2046_PC1_Multipotency_plot <- bulk2046 %>% 
  ggplot(aes(x = Multipotency_Score, y = PC1)) + 
  xlab('B-ALL Multipotency Score (99 Genes)') + 
  geom_point(size=0.7) + stat_cor(r.digits = 4) + theme_pubr()

PC1model_CVplot + bulk2046_PC1_Multipotency_plot + plot_layout(widths=c(0.25,0.75))
ggsave('BALL_MultipotencyScore_Figures/MultipotencyScore_PC1_ModelPerformance.pdf', height = 4.5, width=8)

bulk2046 %>% write_csv('BALL2046_DevState_Updated_May2024_AZ.csv')
bulk2046

Multipotency Score in Normal B cell Development

library(DESeq2)
BDev_pseudobulk <- readRDS('../../BDevelopment_Pseudobulk_byTissue_CellType.rds')
# vst normalize
BDev_pseudobulk[['RNA']]@data <- DESeqDataSetFromMatrix(BDev_pseudobulk[['RNA']]@counts, colData = BDev_pseudobulk@meta.data, design = ~1) %>% vst() %>% assay()
converting counts to integer mode
BDev_pseudobulk[['RNA']]@data[1:10,1:5]
          Ainciburu2022__Bone Marrow__CLP Ainciburu2022__Bone Marrow__Early_GMP Ainciburu2022__Bone Marrow__HSC_MPP Ainciburu2022__Bone Marrow__HSC_MPP_Unk
FAM87B                           3.628009                              3.628009                            3.628009                                3.628009
LINC00115                        4.180853                              4.025843                            4.093941                                4.164805
FAM41C                           4.465845                              4.359141                            4.282165                                4.318407
SAMD11                           3.628009                              3.841141                            3.764684                                3.628009
NOC2L                            5.541047                              5.564924                            5.255788                                4.725258
KLHL17                           3.628009                              3.771774                            3.679684                                3.628009
PLEKHN1                          3.628009                              3.628009                            3.701084                                3.628009
PERM1                            3.628009                              3.692324                            3.628009                                3.628009
HES4                             3.628009                              3.628009                            3.628009                                3.628009
ISG15                            5.918573                              5.790165                            5.371075                                5.569232
          Ainciburu2022__Bone Marrow__Immature_B
FAM87B                                  3.628009
LINC00115                               3.628009
FAM41C                                  3.628009
SAMD11                                  3.628009
NOC2L                                   3.628009
KLHL17                                  3.628009
PLEKHN1                                 3.628009
PERM1                                   3.628009
HES4                                    3.628009
ISG15                                   3.628009
# Calculate Multipotency score
BDev_pseudobulk@meta.data <- bind_cols(BDev_pseudobulk@meta.data, 
                                       calculate_DevState_scores(BDev_pseudobulk[['RNA']]@data, modelweights_withMultipotency, scale = T, sampleID = 'Sample') %>% column_to_rownames('Sample'))
BDev_pseudobulk@meta.data
BDev_pseudobulk@meta.data %>% 
  filter(nCells >= 50) %>% 
  mutate(BDevelopment_CellType_Comprehensive = BDevelopment_CellType_Comprehensive %>% 
           factor(levels = c('HSC/MPP', 'MPP-MyLy', 'LMPP', 'Early GMP', 'Pre-pDC', 'Pre-pDC Cycling', 'pDC', 'MLP', 
                             'CLP', 'Pre-Pro-B', 'Pro-B VDJ', 'Pro-B Cycling 1', 'Pro-B Cycling 2',  # 'Pre-Pro-B Cycling', 
                             'Large Pre-B 1', 'Large Pre-B 2', 'Small Pre-B', 'Immature B', 'Mature B'))) %>%  #Mature B Cycling
  mutate(`Developmental State` = ifelse(BDevelopment_CellType_Comprehensive %in% c('HSC/MPP', 'MPP-MyLy', 'LMPP'), 'HSC/MPP',
                                        ifelse(BDevelopment_CellType_Comprehensive %in% c('Early GMP'), 'Myeloid Progenitor',
                                               ifelse(BDevelopment_CellType_Comprehensive %in% c('Pre-pDC', 'Pre-pDC Cycling', 'pDC'), 'Pre-pDC',
                                                   ifelse(BDevelopment_CellType_Comprehensive %in% c('MLP', 'CLP', 'Pre-Pro-B', 'Pre-Pro-B Cycling'), 'Early Lymphoid',
                                                          ifelse(BDevelopment_CellType_Comprehensive %in% c('Pro-B VDJ', 'Pro-B Cycling 1', 'Pro-B Cycling 2'), 'Pro-B',
                                                                 ifelse(BDevelopment_CellType_Comprehensive %in% c('Large Pre-B 1', 'Large Pre-B 2', 'Small Pre-B'), 'Pre-B',
                                                                        ifelse(BDevelopment_CellType_Comprehensive %in% c('Immature B', 'Mature B', 'Mature B Cycling'), 'Mature B', 'NA'))))))) %>% 
           factor(levels = c('HSC/MPP', 'Myeloid Progenitor', 'Pre-pDC', 'Early Lymphoid', 'Pro-B', 'Pre-B', 'Mature B'))) %>% 
  filter(!is.na(BDevelopment_CellType_Comprehensive)) %>% 
  ggplot(aes(x = BDevelopment_CellType_Comprehensive, y = Multipotency_Score, fill = `Developmental State`)) + 
  geom_hline(yintercept=0, lty=5, size=0.5, alpha=0.8) + geom_boxplot(outlier.size=0.3) + 
  theme_pubr(legend='right') + theme(axis.text.x = element_text(angle=90, hjust=1, size=10.5)) + 
  xlab('\nNormal Population (B Cell Development Atlas)') + ylab('B-ALL Multipotency Score') + 
  scale_fill_brewer(palette = 'Dark2')

ggsave('BALL_MultipotencyScore_Figures/MultipotencyScore_Normal_BDev_pseudobulkScores.pdf', height = 5, width=9.5)

Evaluate 99-gene Multipotency Score within 2046 cohort

bulk2046 <- read_csv('BALL2046_DevState_Updated_May2024_AZ.csv')

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  Age = col_double(),
  WBC = col_double(),
  oscensor = col_double(),
  ostime = col_double(),
  efscensor = col_double(),
  efstime = col_double(),
  HSC_MPP = col_double(),
  Myeloid_Prog = col_double(),
  Pre_pDC = col_double(),
  Early_Lymphoid = col_double(),
  Pro_B = col_double(),
  Pre_B = col_double(),
  Mature_B = col_double(),
  T_NK = col_double(),
  Monocyte = col_double(),
  Erythroid = col_double(),
  PC1 = col_double(),
  Multipotency_Score = col_double()
)
ℹ Use `spec()` for the full column specifications.

Evaluate on Clinical Risk Group

bulk2046$Risk_Group %>% table()
.
       Adult          AYA Childhood HR Childhood SR 
         385          430          680          527 
p <- bulk2046 %>% 
    select(Patient, Risk_Group, Multipotency_Score) %>% pivot_longer(-c(Patient, Risk_Group), names_to='Lineage', values_to='Score') %>%
    filter(Risk_Group != 'NA') %>% mutate(Risk_Group = Risk_Group %>% factor(levels = c('Childhood SR', 'Childhood HR', 'AYA', 'Adult'))) %>% 
    filter(Lineage %in% c('Multipotency_Score')) %>% 
    mutate(Lineage = Lineage %>% str_replace('_', ' ')) %>% 
    ggplot(aes(x = Risk_Group, y = Score, fill = Risk_Group)) + 
    geom_hline(yintercept = 0, alpha = 0.7, lty = 2) + 
    geom_boxplot(outlier.size=0) + ggbeeswarm::geom_quasirandom(aes(size = Risk_Group), width=0.3) + 
    facet_wrap(.~Lineage, scales='free', ncol=5) + 
    theme_pubr(legend = 'none') + 
    scale_fill_brewer(palette = 'Dark2') + 
    theme(strip.text.x = element_text(size = 14), axis.text.x = element_text(size = 11), axis.text.y = element_text(size = 12), axis.title = element_text(size = 12.5)) + 
    scale_size_manual(values = c(0.2, 0.2, 0.3, 0.3)) + xlab('\nClinical Risk Group') + ylab('B-ALL Multipotency Score') + 
    stat_compare_means(comparisons = list(c('Childhood SR', 'Childhood HR'), c('Childhood HR', 'AYA'), c('AYA', 'Adult')))

p
ggsave('BALL_MultipotencyScore_Figures/RiskGroup_2022patients_Multipotency_Score.pdf', device = 'pdf', height = 5.5, width = 4.8)

sum(!is.na(bulk2046$Age ))
[1] 2019
p <- bulk2046 %>% select(Age, Multipotency_Score) %>% pivot_longer(-Age) %>% 
    mutate(`Developmental State` = name %>% str_replace('_',' ')) %>%
    #mutate(`Developmental State` = ifelse(name %>% str_detect('Early'), 'Early Lymphoid',
    #                                     ifelse(name %>% str_detect('Pro_B'), 'Pro-B', 'NA'))) %>% 
    ggplot(aes(x = Age, y = value, color = `Developmental State`)) + 
    geom_smooth(method = 'loess', se = T, size = 2) + #geom_point(size = 0.3, alpha = 0.5) + 
    scale_x_sqrt(breaks = c(1, 5, 10, 15, 20, 30, 40, 50, 60, 70, 80)) + 
    scale_color_brewer(palette = 'Dark2') + 
    geom_hline(yintercept = 0, lty = 3) + 
    geom_vline(xintercept = 1, lty = 3) + geom_vline(xintercept = 15, lty = 3) + geom_vline(xintercept = 40, lty = 3) + 
    xlab('Age at Diagnosis (Years)') + ylab('B-ALL Multipotency Score') +
    ggpubr::theme_pubr(legend = 'top') + theme(legend.title = element_blank())
        
p
ggsave('BALL_MultipotencyScore_Figures/Age_vs_MultipotencyScore_2019patients.pdf', height = 4.2, width = 5.5)

p <- bulk2046 %>% select(Age, Multipotency_Score) %>% pivot_longer(-Age) %>% 
    mutate(`Developmental State` = name %>% str_replace('_',' ')) %>%
    #mutate(`Developmental State` = ifelse(name %>% str_detect('Early'), 'Early Lymphoid',
    #                                     ifelse(name %>% str_detect('Pro_B'), 'Pro-B', 'NA'))) %>% 
    ggplot(aes(x = Age, y = value, color = `Developmental State`)) + 
    geom_smooth(method = 'loess', se = T, size = 2) + #geom_point(size = 0.3, alpha = 0.5) + 
    scale_x_sqrt(breaks = c(1, 5, 10, 15, 20, 30, 40, 50, 60, 70, 80)) + 
    scale_color_brewer(palette = 'Dark2') + 
    geom_hline(yintercept = 0, lty = 3) + 
    geom_vline(xintercept = 1, lty = 3) + geom_vline(xintercept = 18, lty = 3) + geom_vline(xintercept = 40, lty = 3) + 
    xlab('Age at Diagnosis (Years)') + ylab('B-ALL Multipotency Score') +
    ggpubr::theme_pubr(legend = 'top') + theme(legend.title = element_blank())
Error in `select()`:
! Can't subset columns that don't exist.
✖ Column `Multipotency_Score` doesn't exist.
Backtrace:
 1. ... %>% ...
 6. dplyr:::select.data.frame(., Age, Multipotency_Score)

Evaluate on MRD

# Formatted and pivoted
bulk2046_MRD <- bulk2046 %>% 
  select(Patient, MRD_D29_2cat, MRD_D29_3cat, MRD_D29_5cat,  MRD_D46_2cat, 
         c('HSC_MPP', 'Early_Lymphoid', 'Myeloid_Prog', 'Pre_pDC', 'Pro_B', 'Pre_B', 'Mature_B', 'T_NK', 'Monocyte', 'Erythroid',
           'PC1', 'Multipotency_Score')) %>% 
  mutate(MRD_D29_2cat = factor(MRD_D29_2cat, levels = c('Negative\n< 0.01%', 'Positive\n> 0.01%')), 
         MRD_D29_3cat = factor(MRD_D29_3cat, levels = c('< 0.01%', '0.01 - 1%',  '> 1%')), 
         MRD_D29_5cat = factor(MRD_D29_5cat, levels = c('< 0.01%', '0.01 - 0.1%', '0.1 - 1%', '1 - 10%', '> 10%'))) %>% 
  pivot_longer(-c(Patient, MRD_D29_2cat, MRD_D29_3cat, MRD_D29_5cat,  MRD_D46_2cat), names_to = 'Lineage', values_to = 'Score') %>% 
  select(Patient, Lineage, Score, everything()) %>% 
  mutate(Lineage = Lineage %>% #str_replace('NMF.*_', '') %>% 
                factor(levels = c('Multipotency_Score', 'PC1', 'HSC_MPP', 'Early_Lymphoid', 'Myeloid_Prog', 'Pre_pDC', 'Pro_B', 'Pre_B', 
                                  'Mature_B', 'T_NK', 'Monocyte', 'Erythroid'))) %>% 
  arrange(Lineage)

bulk2046_MRD
p <- bulk2046_MRD %>% 
    filter(MRD_D29_5cat != 'NA') %>% #mutate(Score = ifelse(Lineage %in% c('PC1_a', 'PC1_b', 'PC1_c'), -Score, Score)) %>% 
    filter(Lineage %in% c('Multipotency_Score')) %>% 
    mutate(Lineage = Lineage %>% str_replace('_', ' ')) %>% 
    ggplot(aes(x = MRD_D29_5cat, y = Score, fill = MRD_D29_5cat)) + 
    geom_hline(yintercept = 0, alpha = 0.7, lty = 2) + 
    geom_boxplot(outlier.size=0) + ggbeeswarm::geom_quasirandom(aes(size = MRD_D29_5cat), width=0.3) + 
    facet_wrap(.~Lineage, scales='free', ncol=5) + 
    theme_pubr(legend = 'none') + 
    scale_fill_manual(values = c('#7CA987', '#D9D17D', '#DAA15E', '#FF7F50', '#D43F00')) + 
    theme(strip.text.x = element_text(size = 14), axis.text.x = element_text(size = 11), axis.text.y = element_text(size = 12), axis.title = element_text(size = 12.5)) + 
    scale_size_manual(values = c(0.15, 0.4, 0.4, 0.5, 0.5)) + xlab('\nResidual Disease - Day 29 Induction') + ylab('B-ALL Multipotency Score') + 
    stat_compare_means(comparisons = list(c('< 0.01%', '0.01 - 0.1%'), c('< 0.01%', '0.1 - 1%'), c('< 0.01%', '1 - 10%'), c('< 0.01%', '> 10%')))

p
ggsave('BALL_MultipotencyScore_Figures/MRD_levels_794patients_Multipotency_Score.pdf', device = 'pdf', height = 5.5, width = 4.8)

p <- bulk2046_MRD %>% 
    filter(MRD_D29_2cat != 'NA') %>% 
    filter(Lineage %in% c('Multipotency_Score')) %>% 
    mutate(Lineage = Lineage %>% str_replace('_', ' ')) %>% 
    ggplot(aes(x = MRD_D29_2cat, y = Score, fill = MRD_D29_2cat)) + 
    geom_hline(yintercept = 0, alpha = 0.7, lty = 2) + 
    geom_boxplot(outlier.size=0) + ggbeeswarm::geom_quasirandom(size = 0.25, width=0.3) + 
    facet_wrap(.~Lineage, scales='free', ncol=5) + 
    theme_pubr(legend = 'none') + 
    scale_fill_manual(values = c('#7CA987', '#D97A6D')) + 
    theme(strip.text.x = element_text(size = 13.5), axis.text.x = element_text(size = 12), axis.text.y = element_text(size = 12), axis.title = element_text(size = 12.5)) + 
    xlab('\nResidual Disease - Day 29 Induction') + ylab('B-ALL Multipotency Score') + 
    stat_compare_means(comparisons = list(c('Negative\n< 0.01%', 'Positive\n> 0.01%')))

p
ggsave('BALL_MultipotencyScore_Figures/MRD_Status_1197patients_Multipotency_Score.pdf', device = 'pdf', height = 5, width = 4)

Evaluate on Survival Outcomes

Pediatric Survival

First in Pediatric B-ALL Survival outcomes are available for 1,039 pediatric B-ALL patients within the St Jude and COG cohorts. Note that the COG patient samples subject for RNA-seq were preselected to be high risk and consequentially these COG outcomes are worse than the “real world”

# Keep pediatric cohorts (COG and St Jude)
bulk2046_pediatric <- bulk2046 %>% filter(Institute %in% c('St Jude', 'COG')) %>%
    filter(!is.na(oscensor) & !is.na(efscensor)) %>% 
    mutate(Risk_Group = factor(Risk_Group, levels = c('Childhood SR', 'Childhood HR', 'AYA')),  # clinical risk group
           GenomicRisk_pediatric = factor(GenomicRisk_pediatric, levels = c('Unclassified', 'Favorable', 'Intermediate', 'Unfavorable')))     # genomic risk group

bulk2046_pediatric

Loop through developmental states and perform univariable + multivariable cox regression

# dataframe to store results
lineage_survival_ped <- data.frame()
devstates <- c('Multipotency_Score', 'HSC_MPP', 'Myeloid_Prog', 'Pre_pDC', 'Early_Lymphoid', 'Pro_B', 'Pre_B', 'Mature_B', 'T_NK', 'Monocyte', 'Erythroid')

for(lineage in devstates){

    # Univariable model 
    mod_os <- coxph(Surv(ostime, oscensor) ~ get(lineage), bulk2046_pediatric)
    mod_efs <- coxph(Surv(efstime, efscensor) ~ get(lineage), bulk2046_pediatric)

    lineage_survival_ped <- lineage_survival_ped %>% bind_rows(
        summary(mod_os)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = FALSE, survival = 'OS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_os$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue)
    ) %>% bind_rows(
        summary(mod_efs)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = FALSE, survival = 'EFS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_efs$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue)
    )
    
    # Multivariable model 
    mod_multi_os <- coxph(Surv(ostime, oscensor) ~ get(lineage) + Risk_Group + GenomicRisk_pediatric + Sex + Age + WBC, bulk2046_pediatric)
    mod_multi_efs <- coxph(Surv(efstime, efscensor) ~ get(lineage) + Risk_Group + GenomicRisk_pediatric + Sex + Age + WBC, bulk2046_pediatric)
    
    # Create baseline models without dev state abundance to compare against by nested LRT
    mod_multi_os_0 <- coxph(Surv(ostime, oscensor) ~ Risk_Group + GenomicRisk_pediatric + Sex + Age + WBC, bulk2046_pediatric)
    mod_multi_efs_0 <- coxph(Surv(efstime, efscensor) ~ Risk_Group + GenomicRisk_pediatric + Sex + Age + WBC , bulk2046_pediatric)

    lineage_survival_ped <- lineage_survival_ped %>% bind_rows(
        summary(mod_multi_os)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = TRUE, survival = 'OS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_multi_os$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue) %>% 
            # nested LRT: how much prognostic info does dev state add beyond baseline model?
            mutate(pvalue_nestedLRT = anova(mod_multi_os, mod_multi_os_0)[2,'P(>|Chi|)'])
        
    ) %>% bind_rows(
      
        summary(mod_multi_efs)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = TRUE, survival = 'EFS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_multi_efs$n) %>%
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue) %>% 
            # nested LRT: how much prognostic info does dev state add beyond baseline model?
            mutate(pvalue_nestedLRT = anova(mod_multi_efs, mod_multi_efs_0)[2,'P(>|Chi|)'])
        
    )
}

lineage_survival_ped 
surv_ped_uni <- lineage_survival_ped %>% mutate(logpval = ifelse(HR > 1, -log10(pvalue), log10(pvalue))) %>% 
    mutate(Association = ifelse(HR_lwr > 1 & pvalue < 0.05, 'Adverse', ifelse(HR_upr < 1 & pvalue < 0.05, 'Favorable', 'N.S.'))) %>%
    filter(multivariable == FALSE) %>% 
    mutate(survname = ifelse(survival == 'OS', 'Overall Survival', ifelse(survival == 'EFS', 'Event-Free Survival', 'NA')) %>% 
           factor(levels = c('Overall Survival', 'Event-Free Survival'))) %>%
    mutate(Lineage = ifelse(Lineage %in% c('Myeloid_Prog', 'Early_Lymphoid', 'Mature_B', 'Multipotency_Score'), Lineage %>% str_replace('Prog', 'Progenitor') %>% str_replace('_', ' '), 
                            Lineage %>% str_replace('_MPP', '/MPP') %>% str_replace('_NK', '/NK') %>% str_replace('_', '-')) %>% 
             factor(levels = c('Multipotency Score', 'HSC/MPP', 'Myeloid Progenitor', 'Pre-pDC', 'Early Lymphoid', 'Pro-B', 'Pre-B'))) %>% filter(Lineage != 'NA') %>%
    ggplot(aes(x = Lineage, y = HR, ymax = HR_upr, ymin = HR_lwr, color = Association)) + 
    geom_hline(yintercept = 1, lty = 2, size = 0.5, alpha = 1, color = 'darkgrey') + 
    geom_pointrange(size=0.8, position=position_dodge(width=c(0))) +
    facet_wrap(.~survname, ncol=1, scales = 'free_x') + 
    scale_color_manual(values=c('indianred4', 'darkgreen', '#666666')) +   
    theme_pubr(legend = 'right') + scale_y_continuous(breaks = scales::pretty_breaks(n=8), limits = c(0.3,1.8)) + theme(axis.title.x= element_blank()) + 
    ylab('Univariable Hazard Ratio  (per 1SD increase in abundance)') + 
    theme(axis.text.x = element_text(size = 10.5, angle = 90, hjust = 1, vjust = 0.5), strip.text.x = element_text(size=11), axis.text.y = element_text(size=10.5),
          title = element_text(size = 10.5)) + 
    ggtitle('Univariable Survival (n = 1039)')
    
surv_ped_multi <- lineage_survival_ped %>% mutate(logpval = ifelse(HR > 1, -log10(pvalue), log10(pvalue))) %>% 
    mutate(Association = ifelse(HR_lwr > 1 & pvalue_nestedLRT < 0.05, 'Adverse', ifelse(HR_upr < 1 & pvalue_nestedLRT < 0.05, 'Favorable', 'N.S.'))) %>%
    filter(multivariable == TRUE) %>% 
    mutate(survname = ifelse(survival == 'OS', 'Overall Survival', ifelse(survival == 'EFS', 'Event-Free Survival', 'NA')) %>% 
           factor(levels = c('Overall Survival', 'Event-Free Survival'))) %>%
    mutate(Lineage = ifelse(Lineage %in% c('Myeloid_Prog', 'Early_Lymphoid', 'Mature_B', 'Multipotency_Score'), Lineage %>% str_replace('Prog', 'Progenitor') %>% str_replace('_', ' '), 
                            Lineage %>% str_replace('_MPP', '/MPP') %>% str_replace('_NK', '/NK') %>% str_replace('_', '-')) %>% 
             factor(levels = c('Multipotency Score','HSC/MPP', 'Myeloid Progenitor', 'Pre-pDC', 'Early Lymphoid', 'Pro-B', 'Pre-B'))) %>% filter(Lineage != 'NA') %>%
    ggplot(aes(x = Lineage, y = HR, ymax = HR_upr, ymin = HR_lwr, color = Association)) + 
    geom_hline(yintercept = 1, lty = 2, size = 0.5, alpha = 1, color = 'darkgrey') + 
    geom_pointrange(size=0.8, position=position_dodge(width=c(0))) +
    facet_wrap(.~survname, ncol=1, scales = 'free_x') + 
    scale_color_manual(values=c('indianred4', 'darkgreen', '#666666')) +   
    theme_pubr(legend = 'right') + scale_y_continuous(breaks = scales::pretty_breaks(n=8), limits = c(0.3,1.8)) + theme(axis.title.x= element_blank()) + 
    ylab('Multivariable Hazard Ratio  (per 1SD increase in abundance)') + 
    theme(axis.text.x = element_text(size = 10.5, angle = 90, hjust = 1, vjust = 0.5), strip.text.x = element_text(size=11), axis.text.y = element_text(size=10.5),
          title = element_text(size = 10.5)) + 
    ggtitle('Multivariable Survival (n = 1010)')
    
ggsave(plot = surv_ped_uni, filename = 'BALL_survival_figures_final/Survival_pediatric_Univariable_BDevOnly_HazardRatios.pdf', device = 'pdf', height = 7.5, width = 4.5)
ggsave(plot = surv_ped_multi, filename = 'BALL_survival_figures_final/Survival_pediatric_Multivariable_BDevOnly_HazardRatios.pdf', device = 'pdf', height = 7.5, width = 4.5)

surv_ped_uni | surv_ped_multi

bulk2046_pediatric$MRD_D29_2cat %>% table()
.
Negative\n< 0.01% Positive\n> 0.01% 
              650               265 

Pediatric Survival; MRD negative only

Repeat Pediatric Analysis within MRD negative patients

bulk2046_pediatric_MRDneg <- bulk2046_pediatric %>% filter(MRD_D29_2cat == 'Negative\n< 0.01%')

# dataframe to store results
lineage_survival_ped_MRDneg <- data.frame()
devstates <- c('Multipotency_Score', 'HSC_MPP', 'Myeloid_Prog', 'Pre_pDC', 'Early_Lymphoid', 'Pro_B', 'Pre_B', 'Mature_B', 'T_NK', 'Monocyte', 'Erythroid')

for(lineage in devstates){

    # Multivariable model 
    mod_multi_os <- coxph(Surv(ostime, oscensor) ~ get(lineage) + Risk_Group + GenomicRisk_pediatric + Sex + Age + WBC, bulk2046_pediatric_MRDneg)
    mod_multi_efs <- coxph(Surv(efstime, efscensor) ~ get(lineage) + Risk_Group + GenomicRisk_pediatric + Sex + Age + WBC , bulk2046_pediatric_MRDneg)
    
    # Create baseline models without dev state abundance to compare against by nested LRT
    mod_multi_os_0 <- coxph(Surv(ostime, oscensor) ~ Risk_Group + GenomicRisk_pediatric + Sex + Age + WBC, bulk2046_pediatric_MRDneg)
    mod_multi_efs_0 <- coxph(Surv(efstime, efscensor) ~ Risk_Group + GenomicRisk_pediatric + Sex + Age + WBC , bulk2046_pediatric_MRDneg)

    lineage_survival_ped_MRDneg <- lineage_survival_ped_MRDneg %>% bind_rows(
        summary(mod_multi_os)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = TRUE, survival = 'OS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_multi_os$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue) %>% 
            # nested LRT: how much prognostic info does dev state add beyond baseline model?
            mutate(pvalue_nestedLRT = anova(mod_multi_os, mod_multi_os_0)[2,'P(>|Chi|)'])
        
    ) %>% bind_rows(
      
        summary(mod_multi_efs)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = TRUE, survival = 'EFS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_multi_efs$n) %>%
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue) %>% 
            # nested LRT: how much prognostic info does dev state add beyond baseline model?
            mutate(pvalue_nestedLRT = anova(mod_multi_efs, mod_multi_efs_0)[2,'P(>|Chi|)'])
        
    )
}

lineage_survival_ped_MRDneg 
surv_ped_multi_MRDneg <- lineage_survival_ped_MRDneg %>% mutate(logpval = ifelse(HR > 1, -log10(pvalue), log10(pvalue))) %>% 
    mutate(Association = ifelse(HR_lwr > 1 & pvalue_nestedLRT < 0.05, 'Adverse', ifelse(HR_upr < 1 & pvalue_nestedLRT < 0.05, 'Favorable', 'N.S.'))) %>%
    filter(multivariable == TRUE) %>% 
    mutate(survname = ifelse(survival == 'OS', 'Overall Survival', ifelse(survival == 'EFS', 'Event-Free Survival', 'NA')) %>% 
           factor(levels = c('Overall Survival', 'Event-Free Survival'))) %>%
    mutate(Lineage = ifelse(Lineage %in% c('Myeloid_Prog', 'Early_Lymphoid', 'Mature_B', 'Multipotency_Score'), Lineage %>% str_replace('Prog', 'Progenitor') %>% str_replace('_', ' '), 
                            Lineage %>% str_replace('_MPP', '/MPP') %>% str_replace('_NK', '/NK') %>% str_replace('_', '-')) %>% 
             factor(levels = c('Multipotency Score','HSC/MPP', 'Myeloid Progenitor', 'Pre-pDC', 'Early Lymphoid', 'Pro-B', 'Pre-B'))) %>% filter(Lineage != 'NA') %>%
    ggplot(aes(x = Lineage, y = HR, ymax = HR_upr, ymin = HR_lwr, color = Association)) + 
    geom_hline(yintercept = 1, lty = 2, size = 0.5, alpha = 1, color = 'darkgrey') + 
    geom_pointrange(size=0.8, position=position_dodge(width=c(0))) +
    facet_wrap(.~survname, ncol=1, scales = 'free_x') + 
    scale_color_manual(values=c('indianred4', 'darkgreen', '#666666')) +   
    theme_pubr(legend = 'right') + scale_y_continuous(breaks = scales::pretty_breaks(n=8), limits = c(0.2,2.1)) + theme(axis.title.x= element_blank()) + 
    ylab('Multivariable Hazard Ratio  (per 1SD increase in abundance)') + 
    theme(axis.text.x = element_text(size = 10.5, angle = 90, hjust = 1, vjust = 0.5), strip.text.x = element_text(size=11), axis.text.y = element_text(size=10.5),
          title = element_text(size = 10.5)) + 
    ggtitle('Multivariable Survival\n(MRD negative patients; n = 649)')
    
#ggsave(plot = surv_ped_uni, filename = 'BALL_survival_figures_final/Survival_pediatric_Univariable_BDevOnly_HazardRatios.pdf', device = 'pdf', height = 7.5, width = 4.5)
#ggsave(plot = surv_ped_multi_MRDneg, filename = 'BALL_survival_figures_final/Survival_pediatric_Multivariable_BDevOnly_HazardRatios_MRDnegative.pdf', device = 'pdf', height = 7.5, width = 4.5)

surv_ped_multi_MRDneg

Adult Survival

324 adult B-ALL patients with survival data

# Use adult cohort ECOG
bulk2046_adult <- bulk2046 %>% 
    filter(!Institute %in% c('COG', 'St Jude')) %>% 
    filter(!is.na(oscensor) | !is.na(efscensor)) %>% 
    mutate(Risk_Group = factor(Risk_Group, levels = c('AYA', 'Adult')),  # clinical risk group
           GenomicRisk_adult = factor(GenomicRisk_adult, levels = c('Unclassified', 'Favorable', 'Intermediate', 'Unfavorable')))     # genomic risk group

bulk2046_adult

Loop through developmental states and perform univariable + multivariable cox regression

# dataframe to store results
lineage_survival_adult <- data.frame()
devstates <- c('Multipotency_Score', 'HSC_MPP', 'Myeloid_Prog', 'Pre_pDC', 'Early_Lymphoid', 'Pro_B', 'Pre_B', 'Mature_B', 'T_NK', 'Monocyte', 'Erythroid')

for(lineage in devstates){

    # Univariable model stratifiying on institute and primary subtype
    mod_os <- coxph(Surv(ostime, oscensor) ~ get(lineage), bulk2046_adult)
    mod_efs <- coxph(Surv(efstime, efscensor) ~ get(lineage), bulk2046_adult)

    lineage_survival_adult <- lineage_survival_adult %>% bind_rows(
        summary(mod_os)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = FALSE, survival = 'OS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_os$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue)
    ) %>% bind_rows(
        summary(mod_efs)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = FALSE, survival = 'EFS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_efs$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue)
    )
    
    # Multivariable model stratifiying on institute and primary subtype
    mod_multi_os <- coxph(Surv(ostime, oscensor) ~ get(lineage) + Risk_Group + GenomicRisk_adult + Sex + Age + WBC, bulk2046_adult)
    mod_multi_efs <- coxph(Surv(efstime, efscensor) ~ get(lineage) + Risk_Group + GenomicRisk_adult + Sex + Age + WBC, bulk2046_adult)
    
    # Create baseline models without dev state abundance to compare against by nested LRT
    mod_multi_os_0 <- coxph(Surv(ostime, oscensor) ~ Risk_Group + GenomicRisk_adult + Sex + Age + WBC, bulk2046_adult)
    mod_multi_efs_0 <- coxph(Surv(efstime, efscensor) ~ Risk_Group + GenomicRisk_adult + Sex + Age + WBC, bulk2046_adult)

    lineage_survival_adult <- lineage_survival_adult %>% bind_rows(
        summary(mod_multi_os)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = TRUE, survival = 'OS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_multi_os$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue) %>% 
            # nested LRT: how much prognostic info does dev state add beyond baseline model?
            mutate(pvalue_nestedLRT = anova(mod_multi_os, mod_multi_os_0)[2,'P(>|Chi|)'])
        
    ) %>% bind_rows(
      
        summary(mod_multi_efs)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = TRUE, survival = 'EFS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_multi_efs$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue) %>% 
            # nested LRT: how much prognostic info does dev state add beyond baseline model?
            mutate(pvalue_nestedLRT = anova(mod_multi_efs, mod_multi_efs_0)[2,'P(>|Chi|)'])
        
    )
}

lineage_survival_adult
surv_adult_uni <- lineage_survival_adult %>% mutate(logpval = ifelse(HR > 1, -log10(pvalue), log10(pvalue))) %>% 
    mutate(Association = ifelse(HR_lwr > 1 & pvalue < 0.05, 'Adverse', ifelse(HR_upr < 1 & pvalue < 0.05, 'Favorable', 'N.S.'))) %>%
    filter(multivariable == FALSE) %>% 
    mutate(survname = ifelse(survival == 'OS', 'Overall Survival', ifelse(survival == 'EFS', 'Event-Free Survival', 'NA')) %>% 
           factor(levels = c('Overall Survival', 'Event-Free Survival'))) %>%
    mutate(Lineage = ifelse(Lineage %in% c('Myeloid_Prog', 'Early_Lymphoid', 'Mature_B', 'Multipotency_Score'), Lineage %>% str_replace('Prog', 'Progenitor') %>% str_replace('_', ' '), 
                            Lineage %>% str_replace('_MPP', '/MPP') %>% str_replace('_NK', '/NK') %>% str_replace('_', '-')) %>% 
             factor(levels = c('Multipotency Score', 'PC1', 'PC1-new', 'HSC/MPP', 'Myeloid Progenitor', 'Pre-pDC', 'Early Lymphoid', 'Pro-B', 'Pre-B'))) %>% filter(Lineage != 'NA') %>%
    ggplot(aes(x = Lineage, y = HR, ymax = HR_upr, ymin = HR_lwr, color = Association)) + 
    geom_hline(yintercept = 1, lty = 2, size = 0.5, alpha = 1, color = 'darkgrey') + 
    geom_pointrange(size=0.8, position=position_dodge(width=c(0))) +
    facet_wrap(.~survname, ncol=1, scales = 'free_x') + 
    scale_color_manual(values=c('indianred4', 'darkgreen', '#666666')) +   
    theme_pubr(legend = 'right') + scale_y_continuous(breaks = scales::pretty_breaks(n=8), limits = c(0.5,1.5)) + theme(axis.title.x= element_blank()) + 
    ylab('Univariable Hazard Ratio  (per 1SD increase in abundance)') + 
    theme(axis.text.x = element_text(size = 10.5, angle = 90, hjust = 1, vjust = 0.5), strip.text.x = element_text(size=11), axis.text.y = element_text(size=10.5),
          title = element_text(size = 10.5)) + 
    ggtitle('Univariable - Adult B-ALL (n = 324)')
    
surv_adult_multi <- lineage_survival_adult %>% mutate(logpval = ifelse(HR > 1, -log10(pvalue), log10(pvalue))) %>% 
    mutate(Association = ifelse(HR_lwr > 1 & pvalue_nestedLRT < 0.05, 'Adverse', ifelse(HR_upr < 1 & pvalue_nestedLRT < 0.05, 'Favorable', 'N.S.'))) %>%
    filter(multivariable == TRUE) %>% 
    mutate(survname = ifelse(survival == 'OS', 'Overall Survival', ifelse(survival == 'EFS', 'Event-Free Survival', 'NA')) %>% 
           factor(levels = c('Overall Survival', 'Event-Free Survival'))) %>%
    mutate(Lineage = ifelse(Lineage %in% c('Myeloid_Prog', 'Early_Lymphoid', 'Mature_B', 'Multipotency_Score'), Lineage %>% str_replace('Prog', 'Progenitor') %>% str_replace('_', ' '), 
                            Lineage %>% str_replace('_MPP', '/MPP') %>% str_replace('_NK', '/NK') %>% str_replace('_', '-')) %>% 
             factor(levels = c('Multipotency Score', 'PC1', 'PC1-new', 'HSC/MPP', 'Myeloid Progenitor', 'Pre-pDC', 'Early Lymphoid', 'Pro-B', 'Pre-B'))) %>% filter(Lineage != 'NA') %>%
    ggplot(aes(x = Lineage, y = HR, ymax = HR_upr, ymin = HR_lwr, color = Association)) + 
    geom_hline(yintercept = 1, lty = 2, size = 0.5, alpha = 1, color = 'darkgrey') + 
    geom_pointrange(size=0.8, position=position_dodge(width=c(0))) +
    facet_wrap(.~survname, ncol=1, scales = 'free_x') + 
    scale_color_manual(values=c('#666666')) +   
    theme_pubr(legend = 'right') + scale_y_continuous(breaks = scales::pretty_breaks(n=8), limits = c(0.5,1.5)) + theme(axis.title.x= element_blank()) + 
    ylab('Multivariable Hazard Ratio  (per 1SD increase in abundance)') + 
    theme(axis.text.x = element_text(size = 10.5, angle = 90, hjust = 1, vjust = 0.5), strip.text.x = element_text(size=11), axis.text.y = element_text(size=10.5),
          title = element_text(size = 10.5)) + 
    ggtitle('Multivariable - Adult B-ALL (n = 312)')
    
#ggsave(plot = surv_adult_uni, filename = 'BALL_survival_figures_final/Survival_adult_Univariable_BDevOnly_HazardRatios.pdf', device = 'pdf', height = 7.5, width = 4.5)
#ggsave(plot = surv_adult_multi, filename = 'BALL_survival_figures_final/Survival_adult_Multivariable_BDevOnly_HazardRatios.pdf', device = 'pdf', height = 7.5, width = 4.5)

surv_adult_uni | surv_adult_multi

Re-run within BCR::ABL1 patients

# Keep BCR::ABL1 patients
bulk2046_ph <- bulk2046 %>% filter(new_Subtype == 'BCR::ABL1')  

bulk2046_ph
bulk2046_ph %>% filter(oscensor %in% c('0','1')) %>% pull(Institute) %>% table()
.
     CALGB        COG ECOG-ACRIN      MDACC    St Jude       SWOG    Toronto 
        21         26          1          8         21          1          1 

Loop through developmental states and perform univariable + multivariable cox regression

# dataframe to store results
lineage_survival_ph <- data.frame()
devstates <- c('Multipotency_Score', 'HSC_MPP', 'Myeloid_Prog', 'Pre_pDC', 'Early_Lymphoid', 'Pro_B', 'Pre_B')

for(lineage in devstates){

    # Univariable model stratifiying on institute and primary subtype
    mod_os <- coxph(Surv(ostime, oscensor) ~ get(lineage), bulk2046_ph)
    mod_efs <- coxph(Surv(efstime, efscensor) ~ get(lineage), bulk2046_ph)

    lineage_survival_ph <- lineage_survival_ph %>% bind_rows(
        summary(mod_os)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = FALSE, survival = 'OS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_os$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue)
    ) %>% bind_rows(
        summary(mod_efs)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = FALSE, survival = 'EFS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_efs$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue)
    )
    
    # Multivariable model stratifiying on institute and primary subtype
    mod_multi_os <- coxph(Surv(ostime, oscensor) ~ get(lineage) + Risk_Group + Sex + Age + WBC, bulk2046_ph)
    mod_multi_efs <- coxph(Surv(efstime, efscensor) ~ get(lineage) + Risk_Group + Sex + Age + WBC, bulk2046_ph)
    
    # Create baseline models without dev state abundance to compare against by nested LRT
    mod_multi_os_0 <- coxph(Surv(ostime, oscensor) ~ Risk_Group + Sex + Age + WBC, bulk2046_ph)
    mod_multi_efs_0 <- coxph(Surv(efstime, efscensor) ~ Risk_Group + Sex + Age + WBC, bulk2046_ph)

    lineage_survival_ph <- lineage_survival_ph %>% bind_rows(
        summary(mod_multi_os)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = TRUE, survival = 'OS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_multi_os$n) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue) %>% 
            # nested LRT: how much prognostic info does dev state add beyond baseline model?
            mutate(pvalue_nestedLRT = anova(mod_multi_os, mod_multi_os_0)[2,'P(>|Chi|)'])
        
    ) %>% bind_rows(
      
        summary(mod_multi_efs)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = TRUE, survival = 'EFS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse, 
                   n_patients = mod_multi_efs$n) %>%
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, n_patients, statistic, pvalue) %>% 
            # nested LRT: how much prognostic info does dev state add beyond baseline model?
            mutate(pvalue_nestedLRT = anova(mod_multi_efs, mod_multi_efs_0)[2,'P(>|Chi|)'])
        
    )
}

lineage_survival_ph

Make forest plots

surv_ph_uni <- lineage_survival_ph %>% mutate(logpval = ifelse(HR > 1, -log10(pvalue), log10(pvalue))) %>% 
    mutate(Association = ifelse(HR_lwr > 1 & pvalue < 0.05, 'Adverse', ifelse(HR_upr < 1 & pvalue < 0.05, 'Favorable', 'N.S.'))) %>%
    filter(multivariable == FALSE) %>% 
    mutate(survname = ifelse(survival == 'OS', 'Overall Survival', ifelse(survival == 'EFS', 'Event-Free Survival', 'NA')) %>% 
           factor(levels = c('Overall Survival', 'Event-Free Survival'))) %>%
    mutate(Lineage = ifelse(Lineage %in% c('Myeloid_Prog', 'Early_Lymphoid', 'Mature_B', 'Multipotency_Score'), Lineage %>% str_replace('_', ' '), 
                            Lineage %>% str_replace('_MPP', '/MPP') %>% str_replace('_NK', '/NK') %>% str_replace('_', '-')) %>% 
             factor(levels = c('Multipotency Score', 'HSC/MPP', 'Myeloid Prog', 'Pre-pDC', 'Early Lymphoid', 'Pro-B', 'Pre-B'))) %>%  
                               #'Mature B', 'T/NK', 'Monocyte', 'Erythroid', 'Multipotency Score'))) %>% 
    ggplot(aes(x = Lineage, y = HR, ymax = HR_upr, ymin = HR_lwr, color = Association)) + 
    geom_hline(yintercept = 1, lty = 2, size = 0.5, alpha = 1, color = 'darkgrey') + 
    geom_pointrange(size=0.8, position=position_dodge(width=c(0))) +
    facet_wrap(.~survname, ncol=1, scales = 'free_x') + 
    scale_color_manual(values=c('indianred4', '#666666')) +   
    theme_pubr(legend = 'right') + scale_y_continuous(breaks = scales::pretty_breaks(n=8), limits = c(0,2.5)) + theme(axis.title.x= element_blank()) + 
    ylab('Univariable Hazard Ratio  (per 1SD increase in abundance)') + 
    theme(axis.text.x = element_text(size = 10.5, angle = 90, hjust = 1, vjust = 0.5), strip.text.x = element_text(size=11), axis.text.y = element_text(size=10.5),
          title = element_text(size = 10.5)) + 
    ggtitle('Univariable Survival (n = 79)')
    
surv_ph_multi <- lineage_survival_ph %>% mutate(logpval = ifelse(HR > 1, -log10(pvalue), log10(pvalue))) %>% 
    mutate(Association = ifelse(HR_lwr > 1 & pvalue_nestedLRT < 0.05, 'Adverse', ifelse(HR_upr < 1 & pvalue_nestedLRT < 0.05, 'Favorable', 'N.S.'))) %>%
    filter(multivariable == TRUE) %>% 
    mutate(survname = ifelse(survival == 'OS', 'Overall Survival', ifelse(survival == 'EFS', 'Event-Free Survival', 'NA')) %>% 
           factor(levels = c('Overall Survival', 'Event-Free Survival'))) %>%
    mutate(Lineage = ifelse(Lineage %in% c('Myeloid_Prog', 'Early_Lymphoid', 'Mature_B', 'Multipotency_Score'), Lineage %>% str_replace('_', ' '), 
                            Lineage %>% str_replace('_MPP', '/MPP') %>% str_replace('_NK', '/NK') %>% str_replace('_', '-')) %>% 
             factor(levels = c('Multipotency Score', 'HSC/MPP', 'Myeloid Prog', 'Pre-pDC', 'Early Lymphoid', 'Pro-B', 'Pre-B'))) %>%  
                               #'Mature B', 'T/NK', 'Monocyte', 'Erythroid', 'Multipotency Score'))) %>% 
    ggplot(aes(x = Lineage, y = HR, ymax = HR_upr, ymin = HR_lwr, color = Association)) + 
    geom_hline(yintercept = 1, lty = 2, size = 0.5, alpha = 1, color = 'darkgrey') + 
    geom_pointrange(size=0.8, position=position_dodge(width=c(0))) +
    facet_wrap(.~survname, ncol=1, scales = 'free_x') + 
    scale_color_manual(values=c('#666666')) +   
    theme_pubr(legend = 'right') + scale_y_continuous(breaks = scales::pretty_breaks(n=8), limits = c(0,2.5)) + theme(axis.title.x= element_blank()) + 
    ylab('Multivariable Hazard Ratio  (per 1SD increase in abundance)') + 
    theme(axis.text.x = element_text(size = 10.5, angle = 90, hjust = 1, vjust = 0.5), strip.text.x = element_text(size=11), axis.text.y = element_text(size=10.5),
          title = element_text(size = 10.5)) + 
    ggtitle('Multivariable Survival (n = 76)')

ggsave(plot = surv_ph_uni, filename = 'BALL_survival_figures_final/Survival_Ph_BALL2046_Univariable_BDevOnly_HazardRatios.pdf',
     device = 'pdf', height = 7.5, width = 5.2)
ggsave(plot = surv_ph_multi, filename = 'BALL_survival_figures_final/Survival_Ph_BALL2046_Multivariable_BDevOnly_HazardRatios.pdf',
     device = 'pdf', height = 7.5, width = 5.2)

surv_ph_uni | surv_ph_multi

Kim et al BCR::ABL1

kim_phALL_vst <- read_csv('../subtype_subcluster/Kim2023_Ph_BALL/Kim2023_Ph_BALL_RNAseq_vst.csv')

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double(),
  Gene = col_character()
)
ℹ Use `spec()` for the full column specifications.
# calculate NMF scores from vst-normalized data
kim_phALL_vst_DevStatescores <- calculate_DevState_scores(kim_phALL_vst %>% column_to_rownames('Gene') %>% data.matrix() , modelweights_withMultipotency, scale = T, sampleID = 'Sample')
[1] "Warning: 7 genes from Dev State models are missing from query dataset"
kim_phALL_vst_DevStatescores[1:20, ]
ph_BALL_combined <- read_csv('../subtype_subcluster/Kim2023_Ph_BALL/Kim2023_Ph_BALL_anno_cleaned.csv') %>% 
  mutate(Sample = ifelse(Manuscript_name %>% str_detect('-R'), paste0(JAMLR, '_nn_M'), paste0(JAMLR, '_nn_P'))) %>% 
  inner_join(kim_phALL_vst_DevStatescores) 

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  Cohort = col_double(),
  Age_at_dx = col_double(),
  Age_at_dx_rounded = col_double(),
  OS = col_double(),
  alive = col_double(),
  RFS = col_double(),
  relapse_or_death = col_double(),
  OS_BMT_censored = col_double(),
  alive_BMT_censored = col_double(),
  WBC = col_double()
)
ℹ Use `spec()` for the full column specifications.
Joining with `by = join_by(Sample)`
ph_BALL_combined %>% write_csv('Kim2023_Ph_BALL_DevState_Scored_May2024.csv')
ph_BALL_combined

Format for survival

ph_BALL_input <- ph_BALL_combined %>% 
  # exclude a diagnosis - relapse pair
  filter(Survival_analysis == 'yes') %>% filter(Age_at_dx > 19) 

ph_BALL_input
lineage_survival_Ph_Kim <- data.frame()
devstates <- c('Multipotency_Score', 'HSC_MPP', 'Myeloid_Prog', 'Pre_pDC', 'Early_Lymphoid', 'Pro_B', 'Pre_B')

for(lineage in devstates){

    # Univariable model stratifiying on institute and primary subtype
    mod_os <- coxph(Surv(OS, alive) ~ get(lineage), ph_BALL_input)
    mod_rfs <- coxph(Surv(RFS, relapse_or_death) ~ get(lineage), ph_BALL_input)

    lineage_survival_Ph_Kim <- lineage_survival_Ph_Kim %>% bind_rows(
        summary(mod_os)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = FALSE, survival = 'OS') %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, statistic, pvalue)
    ) %>% bind_rows(
        summary(mod_rfs)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = FALSE, survival = 'RFS') %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, statistic, pvalue)
    )
        
    # Multivariable model stratifiying on institute and primary subtype
    mod_multi_os <- coxph(Surv(OS, alive) ~ get(lineage) + sex + Age_at_dx + WBC, ph_BALL_input)
    mod_multi_efs <- coxph(Surv(RFS, relapse_or_death) ~ get(lineage) + sex + Age_at_dx + WBC, ph_BALL_input)
    
    # Create baseline models without dev state abundance to compare against by nested LRT
    mod_multi_os_0 <- coxph(Surv(OS, alive) ~ sex + Age_at_dx + WBC, ph_BALL_input)
    mod_multi_efs_0 <- coxph(Surv(RFS, relapse_or_death) ~ sex + Age_at_dx + WBC, ph_BALL_input)

    lineage_survival_Ph_Kim <- lineage_survival_Ph_Kim %>% bind_rows(
        summary(mod_multi_os)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = TRUE, survival = 'OS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse) %>% 
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, statistic, pvalue) %>% 
            # nested LRT: how much prognostic info does dev state add beyond baseline model?
            mutate(pvalue_nestedLRT = anova(mod_multi_os, mod_multi_os_0)[2,'P(>|Chi|)'])
        
    ) %>% bind_rows(
      
        summary(mod_multi_efs)$coefficients %>% data.frame() %>% dplyr::rename(HR = 'exp.coef.', HRse = 'se.coef.', statistic = 'z', pvalue = 'Pr...z..') %>% 
            rownames_to_column('variable') %>% mutate(Lineage = lineage, multivariable = TRUE, survival = 'RFS') %>% 
            mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse) %>%
            filter(variable %>% str_detect('lineage')) %>% select(Lineage, survival, multivariable, HR, HRse, HR_lwr, HR_upr, statistic, pvalue) %>% 
            # nested LRT: how much prognostic info does dev state add beyond baseline model?
            mutate(pvalue_nestedLRT = anova(mod_multi_efs, mod_multi_efs_0)[2,'P(>|Chi|)'])
        
    )
}

lineage_survival_Ph_Kim
lineage_survival_Ph_Kim %>% mutate(logpval = ifelse(HR > 1, -log10(pvalue), log10(pvalue))) %>% 
    mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse) %>%
    mutate(Association = ifelse(HR_lwr > 1 & pvalue < 0.05, 'Adverse', ifelse(HR_upr < 1 & pvalue < 0.05, 'Favorable', 'N.S.'))) %>%
    filter(multivariable == FALSE) %>% 
    mutate(survname = ifelse(survival == 'OS', 'Overall Survival', ifelse(survival == 'RFS', 'Relapse-Free Survival', 'NA')) %>% 
           factor(levels = c('Overall Survival', 'Relapse-Free Survival'))) %>%
    mutate(Lineage = ifelse(Lineage %in% c('Myeloid_Prog', 'Early_Lymphoid', 'Mature_B', 'Multipotency_Score'), Lineage %>% str_replace('Prog', 'Progenitor') %>% str_replace('PC1_','') %>% str_replace('_', ' '), 
                            Lineage %>% str_replace('_MPP', '/MPP') %>% str_replace('_NK', '/NK') %>% str_replace('_', '-')) %>% 
             factor(levels = c('Multipotency Score', 'HSC/MPP', 'Myeloid Progenitor', 'Pre-pDC', 'Early Lymphoid', 'Pro-B', 'Pre-B'))) %>% 
    ggplot(aes(x = Lineage, y = HR, ymax = HR_upr, ymin = HR_lwr, color = Association)) + 
    geom_hline(yintercept = 1, lty = 2, size = 0.5, alpha = 1, color = 'darkgrey') + 
    geom_pointrange(size=0.8, position=position_dodge(width=c(0))) +
    facet_wrap(.~survname, ncol=1, scales = 'free_x') + 
    scale_color_manual(values=c('indianred4', 'darkgreen', '#666666')) +   #'#744F80', '#303030'
    theme_pubr(legend = 'right') + scale_y_continuous(breaks = scales::pretty_breaks(n=8), limits = c(0,2.5)) + theme(axis.title.x= element_blank()) + 
    ylab('Univariable Hazard Ratio  (per 1SD increase in abundance)') + #ylim(c(0, 2.5)) +
    theme(axis.text.x = element_text(size = 10.5, angle = 90, hjust = 1, vjust = 0.5), strip.text.x = element_text(size=11), axis.text.y = element_text(size=10.5),
          title = element_text(size = 10.5)) + 
    ggtitle('BCR::ABL1 Adult B-ALL (n = 41)')

    
ggsave(filename = 'BALL_survival_figures_final/Survival_Ph_Kim2023_Univariable_BDevOnly_HazardRatios.pdf', device = 'pdf', height = 7.5, width = 4.5)
lineage_survival_Ph_Kim %>% mutate(logpval = ifelse(HR > 1, -log10(pvalue), log10(pvalue))) %>% 
    mutate(HR_upr = HR + 1.96*HRse, HR_lwr = HR - 1.96*HRse) %>%
    mutate(Association = ifelse(HR_lwr > 1 & pvalue_nestedLRT < 0.05, 'Adverse', ifelse(HR_upr < 1 & pvalue_nestedLRT < 0.05, 'Favorable', 'N.S.'))) %>%
    filter(multivariable == TRUE) %>% 
    mutate(survname = ifelse(survival == 'OS', 'Overall Survival', ifelse(survival == 'RFS', 'Relapse-Free Survival', 'NA')) %>% 
           factor(levels = c('Overall Survival', 'Relapse-Free Survival'))) %>%
    mutate(Lineage = ifelse(Lineage %in% c('Myeloid_Prog', 'Early_Lymphoid', 'Mature_B', 'Multipotency_Score'), Lineage %>% str_replace('Prog', 'Progenitor') %>% str_replace('PC1_','') %>% str_replace('_', ' '), 
                            Lineage %>% str_replace('_MPP', '/MPP') %>% str_replace('_NK', '/NK') %>% str_replace('_', '-')) %>% 
             factor(levels = c('Multipotency Score', 'HSC/MPP', 'Myeloid Progenitor', 'Pre-pDC', 'Early Lymphoid', 'Pro-B', 'Pre-B'))) %>% 
    ggplot(aes(x = Lineage, y = HR, ymax = HR_upr, ymin = HR_lwr, color = Association)) + 
    geom_hline(yintercept = 1, lty = 2, size = 0.5, alpha = 1, color = 'darkgrey') + 
    geom_pointrange(size=0.8, position=position_dodge(width=c(0))) +
    facet_wrap(.~survname, ncol=1, scales = 'free_x') + 
    scale_color_manual(values=c('indianred4', '#666666')) +   #'#744F80', '#303030'
    theme_pubr(legend = 'right') + scale_y_continuous(breaks = scales::pretty_breaks(n=8), limits = c(0,2.5)) + theme(axis.title.x= element_blank()) + 
    ylab('Multivariable Hazard Ratio  (per 1SD increase in abundance)') + #ylim(c(0, 2.5)) +
    theme(axis.text.x = element_text(size = 10.5, angle = 90, hjust = 1, vjust = 0.5), strip.text.x = element_text(size=11), axis.text.y = element_text(size=10.5),
          title = element_text(size = 10.5)) + 
    ggtitle('BCR::ABL1 Adult B-ALL (n = 41)')

    
ggsave(filename = 'BALL_survival_figures_final/Survival_Ph_Kim2023_Multivariable_BDevOnly_HazardRatios.pdf', device = 'pdf', height = 7.5, width = 4.5)

Compare with MPAL

Score in B-ALL vs MPAL(B/M)

panleucohort <- readRDS("../../../../../../../../Other/PanLeuGeneExpr/panLeuTotal-rlog.rds")
panleucohort_anno <- data.table::fread('../../../../../../../../Other/PanLeuGeneExpr/PanLeuCohort.csv') 
panleucohort_BALL_MPAL <- panleucohort[,colnames(panleucohort) %in% filter(panleucohort_anno, disease %in% c('B-ALL', 'MPAL'))$sample]
panleucohort_BALL_MPAL %>% dim()
[1] 19032  1319
panleucohort_rlog_DevStatescores <- calculate_DevState_scores(panleucohort_BALL_MPAL, modelweights_withMultipotency, scale = T, sampleID = 'sample')
[1] "Warning: 12 genes from Dev State models are missing from query dataset"
panleucohort_rlog_DevStatescores
panleu_scored <- panleucohort_rlog_DevStatescores %>% left_join(panleucohort_anno)
Joining with `by = join_by(sample)`
panleu_scored %>% write_csv('panleucohort_BALL_MPAL_rlog_DevState_scores_May2024.csv')
panleu_scored

Sanity check between datasets

Check correlation between the two datasets - same samples but presumably different alignments and different normalizations.

panleu_scored %>% filter(disease == 'B-ALL') %>% 
  select(sample, HSC_MPP, Myeloid_Prog, Pre_pDC, Early_Lymphoid, Pro_B, Pre_B, Multipotency_Score) %>% 
  pivot_longer(-sample, values_to = 'rlog') %>% 
  # standardize within B-ALL
  group_by(name) %>% mutate(rlog = (rlog - mean(rlog)) / sd(rlog)) %>% 
  inner_join(
    bulk2046 %>% select(sample = SampleID_old, HSC_MPP, Myeloid_Prog, Pre_pDC, Early_Lymphoid, Pro_B, Pre_B, Multipotency_Score) %>% 
      pivot_longer(-sample, values_to = 'vst')
  ) %>% 
  mutate(name = factor(name, levels = c('Multipotency_Score', 'HSC_MPP', 'Myeloid_Prog', 'Pre_pDC', 'Early_Lymphoid', 'Pro_B', 'Pre_B'))) %>%
  ggplot(aes(x = rlog, y = vst)) + 
  geom_hline(yintercept=0) + geom_vline(xintercept=0) + 
  geom_point(size = 0.5) + geom_smooth(method = 'lm') + stat_cor() + 
  facet_wrap(.~name, ncol = 4) + xlab('rlog normalization (Montefiori et al)') + ylab('vst normalization (B-ALL 2046 cohort)')
Joining with `by = join_by(sample, name)`

Compare Early subsets vs MPAL

pangeneleu_compare <- panleu_scored %>%
  mutate(case_ID = sample %>% str_replace('_.*','')) %>% 
  left_join(read_csv('../subtype_subcluster/Alexander_MPAL_Fusions.csv') %>%
              mutate(ZNF384r = ifelse((gene_a == 'ZNF384') | (gene_b == 'ZNF384'), 1, 0), 
                     KMT2Ar = ifelse((gene_a == 'KMT2A') | (gene_b == 'KMT2A'), 1, 0)) %>% 
              group_by(case_ID) %>% 
              summarise(ZNF384r = ifelse(sum(ZNF384r) >= 1, 'MPAL(ZNF384r)', 'Other'), 
                        KMT2Ar = ifelse(sum(KMT2Ar) >= 1, 'KMT2Ar', 'Other')) %>% unique() ) %>% 
  mutate(disease_subtype = ifelse(is.na(ZNF384r), subtype, ifelse(ZNF384r == 'MPAL(ZNF384r)', ZNF384r, subtype))) %>% 
  left_join( 
    bind_rows(
      read_csv('../subtype_subcluster/KMT2A_subcluster142.csv') %>% mutate(case_ID = Patient %>% str_replace('_.*','')) %>% select(case_ID, subset = Subgroup),
      read_tsv('../subtype_subcluster/DUX4_bulk.txt', col_names=F) %>% select(case_ID = X1, subset = X2))
    ) %>% 
  left_join(
    read_tsv('../survival_analysis_old/Ph_BALL_CellofOrigin_Subtype.tsv') %>% select(sample = Samples, Cluster)
  ) %>% 
  mutate(subset = ifelse(is.na(subset), Cluster, subset))
Warning: Missing column names filled in: 'X9' [9], 'X10' [10], 'X11' [11], 'X12' [12]
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  case_ID = col_character(),
  gene_a = col_character(),
  refseq_a = col_character(),
  `chromosome:position:strand_gene_a` = col_character(),
  gene_b = col_character(),
  refseq_b = col_character(),
  `chromosome:position:strand_gene_b` = col_character(),
  `ALAL subtype` = col_character(),
  X9 = col_logical(),
  X10 = col_logical(),
  X11 = col_logical(),
  X12 = col_logical()
)
Joining with `by = join_by(case_ID)`
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  Patient = col_character(),
  Subtype = col_character(),
  Subgroup = col_character(),
  Subgroup_Name = col_character()
)

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  X1 = col_character(),
  X2 = col_character()
)
Joining with `by = join_by(case_ID)`
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  Samples = col_character(),
  Cluster = col_character()
)
Joining with `by = join_by(sample)`
pangeneleu_compare %>% write_csv("pangeneleu_compare_BALL_MPAL_DevState_May2024.csv")
pangeneleu_compare$subset %>% table()
.
          D1           D2      KMT2A-a      KMT2A-b Ph_Early-Pro Ph_Inter-Pro  Ph_Late-Pro 
          35           48           10           69           50           38           57 
pangeneleu_compare <- read_csv("pangeneleu_compare_BALL_MPAL_DevState_May2024.csv")

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  HSC_MPP = col_double(),
  Myeloid_Prog = col_double(),
  Pre_pDC = col_double(),
  Early_Lymphoid = col_double(),
  Pro_B = col_double(),
  Pre_B = col_double(),
  Mature_B = col_double(),
  Erythroid = col_double(),
  Monocyte = col_double(),
  T_NK = col_double(),
  Multipotency_Score = col_double(),
  ZNF384r = col_logical(),
  KMT2Ar = col_logical()
)
ℹ Use `spec()` for the full column specifications.
Warning: 110 parsing failures.
 row     col           expected actual                                                file
1229 ZNF384r 1/0/T/F/TRUE/FALSE  Other 'pangeneleu_compare_BALL_MPAL_DevState_May2024.csv'
1229 KMT2Ar  1/0/T/F/TRUE/FALSE  Other 'pangeneleu_compare_BALL_MPAL_DevState_May2024.csv'
1230 ZNF384r 1/0/T/F/TRUE/FALSE  Other 'pangeneleu_compare_BALL_MPAL_DevState_May2024.csv'
1230 KMT2Ar  1/0/T/F/TRUE/FALSE  Other 'pangeneleu_compare_BALL_MPAL_DevState_May2024.csv'
1232 ZNF384r 1/0/T/F/TRUE/FALSE  Other 'pangeneleu_compare_BALL_MPAL_DevState_May2024.csv'
.... ....... .................. ...... ...................................................
See problems(...) for more details.
pangeneleu_compare %>% select(disease_subtype, subset) %>% table()
                   subset
disease_subtype     D1 D2 KMT2A-a KMT2A-b Ph_Early-Pro Ph_Inter-Pro Ph_Late-Pro
  B-other            0  0       0       0            0            0           0
  BCL11B             0  0       0       0            0            0           0
  BCL2/MYC           0  0       0       0            0            0           0
  DDX3X-MLLT10       0  0       0       0            0            0           0
  DUX4              35 48       0       0            0            0           0
  ETV6-RUNX1         0  0       0       0            0            0           0
  HLF                0  0       0       0            0            0           0
  HOXA               0  0       0       0            0            0           0
  Hyperdiploid       0  0       0       0            0            0           0
  iAMP21             0  0       0       0            0            0           0
  IKZF1 N159Y        0  0       0       0            0            0           0
  KMT2A              0  0      10      69            0            0           0
  LowHypo            0  0       0       0            0            0           0
  MEF2D              0  0       0       0            0            0           0
  MPAL(AUL)          0  0       0       0            0            0           0
  MPAL(B/M)          0  0       0       0            0            0           0
  MPAL(KMT2Ar)       0  0       0       0            0            0           0
  MPAL(NOS (T/B))    0  0       0       0            0            0           0
  MPAL(NOS (T/B/M))  0  0       0       0            0            0           0
  MPAL(Ph)           0  0       0       0            0            0           0
  MPAL(T/M)          0  0       0       0            0            0           0
  MPAL(ZNF384r)      0  0       0       0            0            0           0
  NUTM1              0  0       0       0            0            0           0
  PAX5 P80R          0  0       0       0            0            0           0
  PAX5alt            0  0       0       0            0            0           0
  Ph                 0  0       0       0           50           38          57
  PICALM-MLLT10      0  0       0       0            0            0           0
  SET-NUP214         0  0       0       0            0            0           0
  TCF3-PBX1          0  0       0       0            0            0           0
  TLX3               0  0       0       0            0            0           0
  Y                  0  0       0       0            0            0           0
  ZEB2/CEBPE         0  0       0       0            0            0           0
  ZNF384             0  0       0       0            0            0           0
# Subset to include B-ALL and B-lineage MPALs
pangeneleu_compare <- pangeneleu_compare %>% filter(disease2 %in% c('B-ALL', 'MPAL(AUL)', 'MPAL(B/M)', 'MPAL(KMT2Ar)', 'MPAL(Ph)')) 
abundances <- c('Multipotency_Score', 'HSC_MPP', 'Early_Lymphoid', 'Myeloid_Prog', 'Pre_pDC', 'Pro_B', 'Pre_B')


# Get categories
MPAL_BALL_categories <- bind_rows(
  # ZNF384
  pangeneleu_compare %>% filter(disease_subtype %>% str_detect('ZNF384')) %>% 
    mutate(disease_category = paste0('ZNF384 ', disease)) %>% 
    select(disease_category, abundances, disease, disease2, subset), 
  # BCR::ABL1
  pangeneleu_compare %>% filter(disease_subtype %>% str_detect('Ph')) %>% 
    mutate(disease_category = paste0('BCR::ABL1 ', ifelse(disease == 'MPAL', 'MPAL',
                                                   ifelse(subset %>% str_detect('Early'), 'Early-Pro', 
                                                          ifelse(subset %>% str_detect('Inter'), 'Inter-Pro',
                                                                 ifelse(subset %>% str_detect('Late'), 'Late-Pro', 'NA')))))) %>% 
    select(disease_category, abundances, disease, disease2, subset) %>% filter(disease_category != 'BCR::ABL1 NA'), 
  # KMT2A
  pangeneleu_compare %>% filter(disease_subtype %>% str_detect('KMT2A')) %>% 
    mutate(disease_category = paste0('KMT2A ', ifelse(disease == 'MPAL', 'MPAL',
                                                      ifelse(subset %>% str_detect('KMT2A-b'), 'Early', 
                                                             ifelse(subset %>% str_detect('KMT2A-a'), 'Committed', 'NA'))))) %>% 
    select(disease_category, abundances, disease, disease2, subset) %>% filter(disease_category != 'KMT2A NA'), 
  # DUX4
  pangeneleu_compare %>% filter(disease_subtype %>% str_detect('DUX4')) %>% 
    mutate(disease_category = ifelse(subset == 'D1', 'DUX4 Committed', ifelse(subset == 'D2', 'DUX4 Early', 'NA'))) %>% 
    select(disease_category, abundances, disease, disease2) %>% filter(disease_category != 'NA'), 
  # Other 
  pangeneleu_compare %>% filter(!disease_subtype %>% str_detect('ZNF384|Ph|KMT2A|DUX4')) %>%
    mutate(disease_category = paste0('Other ', disease)) %>% 
    select(disease_category, abundances, disease, disease2, subset) %>% filter(disease_category != 'NA'), 
  ) 

MPAL_BALL_categories %>% select(disease_category, disease) %>% table()
                     disease
disease_category      B-ALL MPAL
  BCR::ABL1 Early-Pro    50    0
  BCR::ABL1 Inter-Pro    38    0
  BCR::ABL1 Late-Pro     57    0
  BCR::ABL1 MPAL          0    4
  DUX4 Committed         35    0
  DUX4 Early             48    0
  KMT2A Committed        10    0
  KMT2A Early            69    0
  KMT2A MPAL              0   13
  Other B-ALL           793    0
  Other MPAL              0   36
  ZNF384 B-ALL           53    0
  ZNF384 MPAL             0   13
cp_multipotency <- cutpointr(MPAL_BALL_categories, x = Multipotency_Score, class = disease, na.rm = TRUE, 
                method = maximize_metric, metric = sum_sens_spec)
Assuming the positive class is MPAL
Assuming the positive class has higher x values
plot(cp_multipotency)

cutoff <- cp_multipotency$optimal_cutpoint

p <- MPAL_BALL_categories %>% 
   mutate(disease_category = factor(disease_category, levels = rev(c('ZNF384 MPAL', 'ZNF384 B-ALL', 'KMT2A MPAL', 'KMT2A Early', 'BCR::ABL1 MPAL', 'BCR::ABL1 Early-Pro', 
                                                                'Other MPAL', 'DUX4 Early', 'BCR::ABL1 Inter-Pro', 
                                                                'BCR::ABL1 Late-Pro', 'KMT2A Committed', 'DUX4 Committed', 'Other B-ALL')))) %>%
  ggplot(aes(y = disease_category, x = Multipotency_Score, fill=stat(x))) +
    geom_density_ridges_gradient(
        jittered_points = TRUE, scale = 1.7,
        position = position_points_jitter(width = 0, height = 0), 
        point_shape = '|', point_size = 3, point_alpha = 0.3) +
    scale_fill_gradient2(midpoint=cutoff, high='#71305D', low='#5083A2', name = 'Multipotency\nScore') +
    xlab(paste0('Multipotency Score')) +
    theme_pubr() + xlim(-3, 3.7) +
    geom_vline(xintercept = cutoff, lty = 2, alpha=0.5) + 
    ylab('') + 
    theme(legend.position='right', 
          axis.text.y = element_text(size=12, color = c('dodgerblue4', 'dodgerblue4', 'dodgerblue4', 'dodgerblue4', 'dodgerblue4', 'darkgreen', 'indianred4', 
                                                        'darkgreen', 'indianred4', 'darkgreen', 'indianred4', 'darkgreen', 'indianred4')),
          axis.title.x = element_text(size=13)) 
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.
p

# need MPAL_BALL_categories, Ph, KMT2A, ZNF384
plot_BALL_MPAL_categories <- function(value = 'Multipotency_Score', value_name = 'B-ALL Multipotency Score', cutpoint = cutpoint, ylimits = c(-3, 4)){

  p0 <- MPAL_BALL_categories %>% 
    filter(disease %in% c('B-ALL', 'MPAL')) %>% filter(disease_category %>% str_detect('Other')) %>% 
    mutate(category = 'Other Subtypes') %>% mutate(disease_cat = ifelse(disease == 'MPAL', 'Other\nMPAL', 'Other\nB-ALL') %>% factor(levels = c('Other\nMPAL', 'Other\nB-ALL'))) %>% 
    select(category, disease_cat, value) %>% pivot_longer(-c(category, disease_cat)) %>% 
    ggplot(aes(x = disease_cat, y = value, fill = disease_cat)) + geom_hline(yintercept = cutpoint, lty = 2, alpha = 0.7) +
    geom_boxplot(outlier.size = 0, alpha = 0.8) + ggbeeswarm::geom_quasirandom(aes(size = disease_cat), width = 0.3) + 
    scale_fill_manual(values = c('indianred4', 'darkgreen')) + 
    scale_size_manual(values = c(0.8, 0.15)) + 
    facet_wrap(.~category) + 
    stat_compare_means(comparisons = list(c('Other\nMPAL', 'Other\nB-ALL'))) + 
    theme_pubr(legend = 'none') + ylab(value_name) + xlab('\nSubgroup') + ylim(ylimits) +
    theme(strip.text.x = element_text(size = 13), axis.title = element_text(size = 13), axis.text.x = element_text(size = 12))
  
  p1 <- MPAL_BALL_categories %>% filter(disease_category %>% str_detect('BCR::ABL1')) %>% 
    mutate(disease3 = ifelse(disease2 == 'B-ALL', paste0('BCR::ABL1\n', subset %>% str_replace('Ph_','')), 'BCR::ABL1\nMPAL') %>%  
             factor(levels = rev(c('BCR::ABL1\nLate-Pro', 'BCR::ABL1\nInter-Pro', 'BCR::ABL1\nEarly-Pro', 'BCR::ABL1\nMPAL')))) %>% 
    select(disease3, value) %>% #mutate(disease3 = paste0('BCR::ABL1\n',disease3)) %>% 
    pivot_longer(-disease3) %>% 
    mutate(category = 'BCR::ABL1') %>%
    mutate(`Disease Subgroup` = ifelse(disease3 %>% str_detect('MPAL'), '\nMPAL (B/M)\n', 
                                  ifelse(disease3 %>% str_detect('Early|ZNF384'), '\nB-ALL\nEarly/Multipotent\n',
                                         ifelse(disease3 %>% str_detect('Inter'), '\nB-ALL\nInterPro\n', '\nB-ALL\nCommitted\n'))) %>% 
             factor(levels = c('\nMPAL (B/M)\n', '\nB-ALL\nEarly/Multipotent\n', '\nB-ALL\nInterPro\n', '\nB-ALL\nCommitted\n'))) %>% 
    ggplot(aes(x = disease3, y = value, fill = `Disease Subgroup`)) + geom_hline(yintercept = cutpoint, lty = 2, alpha = 0.7) +
    geom_boxplot(outlier.size = 0, alpha = 0.8) + ggbeeswarm::geom_quasirandom(aes(size = `Disease Subgroup`), width = 0.3) + 
    scale_fill_manual(values = c('indianred4', '#FF7F00', '#5EA2CF', '#1061D9')) + 
    scale_size_manual(values = c(1, 0.6, 0.7, 0.7)) + 
    facet_wrap(.~category) + 
    stat_compare_means(comparisons = list(c('BCR::ABL1\nMPAL', 'BCR::ABL1\nEarly-Pro'), 
                                          c('BCR::ABL1\nMPAL', 'BCR::ABL1\nInter-Pro'),
                                          c('BCR::ABL1\nMPAL', 'BCR::ABL1\nLate-Pro'))) + 
    theme_pubr(legend = 'none') + ylab(value_name) + xlab('\nSubgroup') + ylim(ylimits) +
    theme(strip.text.x = element_text(size = 13), axis.title = element_text(size = 13), axis.text.x = element_text(size = 12))
  
  p2 <- MPAL_BALL_categories %>% filter(disease_category %>% str_detect('KMT2A')) %>% 
    mutate(disease3 = ifelse(disease == 'B-ALL', subset, ifelse(disease == 'MPAL', 'KMT2A-r\nMPAL', subset))) %>% 
    mutate(disease3 = disease3 %>% str_replace('KMT2A-a', 'KMT2A-r\nCommitted') %>% str_replace('KMT2A-b', 'KMT2A-r\nEarly') %>% 
             factor(levels = c('KMT2A-r\nMPAL', 'KMT2A-r\nEarly', 'KMT2A-r\nCommitted'))) %>% filter(disease3 != 'NA') %>% 
    select(disease3, value) %>% #mutate(disease3 = paste0('BCR::ABL1\n',disease3)) %>% 
    pivot_longer(-disease3) %>% 
    mutate(category = 'KMT2A-r') %>%
    mutate(`Disease Subgroup` = ifelse(disease3 %>% str_detect('MPAL'), '\nMPAL (B/M)\n', 
                                  ifelse(disease3 %>% str_detect('Early|ZNF384'), '\nB-ALL\nEarly/Multipotent\n', '\nB-ALL\nCommitted\n')) %>% 
             factor(levels = c('\nMPAL (B/M)\n', '\nB-ALL\nEarly/Multipotent\n', '\nB-ALL\nCommitted\n'))) %>% 
    ggplot(aes(x = disease3, y = value, fill = `Disease Subgroup`)) + geom_hline(yintercept = cutpoint, lty = 2, alpha = 0.7) +
    geom_boxplot(outlier.size = 0, alpha = 0.8) + ggbeeswarm::geom_quasirandom(aes(size = `Disease Subgroup`), width = 0.3) + 
    scale_fill_manual(values = c('indianred4', '#B22122', '#1D90FF')) + 
    scale_size_manual(values = c(1, 0.6, 0.8)) + 
    facet_wrap(.~category) + 
    stat_compare_means(comparisons = list(c('KMT2A-r\nMPAL', 'KMT2A-r\nEarly'), c('KMT2A-r\nMPAL', 'KMT2A-r\nCommitted'))) +
    theme_pubr(legend = 'none') + ylab(value_name) + xlab('\nSubgroup') + ylim(ylimits) +
    theme(strip.text.x = element_text(size = 13), axis.title = element_text(size = 13), axis.text.x = element_text(size = 12))
  
  
  p3 <- MPAL_BALL_categories %>% filter(disease_category %>% str_detect('ZNF384')) %>% 
    mutate(disease3 = paste0('ZNF384-r\n',disease) %>% factor(levels = c('ZNF384-r\nMPAL', 'ZNF384-r\nB-ALL'))) %>% select(disease3, value) %>% 
    pivot_longer(-disease3) %>% 
    mutate(category = 'ZNF384-r') %>%
    mutate(`Disease Subgroup` = ifelse(disease3 %>% str_detect('MPAL'), '\nMPAL (B/M)\n', 
                                  ifelse(disease3 %>% str_detect('Early|ZNF384'), '\nB-ALL\nEarly/Multipotent\n', '\nB-ALL\nCommitted\n')) %>% 
             factor(levels = c('\nMPAL (B/M)\n', '\nB-ALL\nEarly/Multipotent\n', '\nB-ALL\nCommitted\n'))) %>% 
    ggplot(aes(x = disease3, y = value, fill = `Disease Subgroup`)) + geom_hline(yintercept = cutpoint, lty = 2, alpha = 0.7) +
    geom_boxplot(outlier.size = 0, alpha = 0.8) + ggbeeswarm::geom_quasirandom(aes(size = `Disease Subgroup`), width = 0.3) + 
    scale_fill_manual(values = c('indianred4', 'dodgerblue4')) + 
    scale_size_manual(values = c(1, 0.7)) + 
    facet_wrap(.~category) + 
    stat_compare_means(comparisons = list(c('ZNF384-r\nMPAL', 'ZNF384-r\nB-ALL'))) + 
    theme_pubr(legend = 'none') + ylab(value_name) + xlab('\nSubgroup') + ylim(ylimits) +
    theme(strip.text.x = element_text(size = 13), axis.title = element_text(size = 13), axis.text.x = element_text(size = 12))
  
  return(ggarrange(p0, p1, p2, p3, ncol = 4, widths = c(0.58, 1, 0.8, 0.58)))
}
plot_BALL_MPAL_categories(value = 'Multipotency_Score', value_name = 'B-ALL Multipotency Score', cutpoint = cp_multipotency$optimal_cutpoint, ylimits = c(-3, 3.75))
Warning: cannot compute exact p-value with ties

ggsave('BALL_MultipotencyScore_Figures/BALL_vs_MPAL_bySubtype_MultipotencyScore.pdf', height = 4.8, width = 15, device = 'pdf')
cp <- cutpointr(MPAL_BALL_categories, x = HSC_MPP, class = disease, na.rm = TRUE, 
                method = maximize_metric, metric = sum_sens_spec)
Assuming the positive class is MPAL
Assuming the positive class has higher x values
plot_BALL_MPAL_categories(value = 'HSC_MPP', value_name = 'HSC/MPP Abundance', cutpoint = cp$optimal_cutpoint, ylimits = c(-2.8, 4))
Warning: cannot compute exact p-value with ties

ggsave('BALL_MultipotencyScore_Figures/BALL_vs_MPAL_bySubtype_HSCMPP.pdf', height = 4.8, width = 15, device = 'pdf')
cp <- cutpointr(MPAL_BALL_categories, x = Early_Lymphoid, class = disease, na.rm = TRUE, 
                method = maximize_metric, metric = sum_sens_spec)
Assuming the positive class is MPAL
Assuming the positive class has higher x values
plot_BALL_MPAL_categories(value = 'Early_Lymphoid', value_name = 'Early Lymphoid Abundance', cutpoint = cp$optimal_cutpoint, ylimits = c(-2.8, 4))
Warning: cannot compute exact p-value with ties

ggsave('BALL_MultipotencyScore_Figures/BALL_vs_MPAL_bySubtype_EarlyLymphoid.pdf', height = 4.8, width = 15, device = 'pdf')
cp <- cutpointr(MPAL_BALL_categories, x = Myeloid_Prog, class = disease, na.rm = TRUE, 
                method = maximize_metric, metric = sum_sens_spec)
Assuming the positive class is MPAL
Assuming the positive class has higher x values
plot_BALL_MPAL_categories(value = 'Myeloid_Prog', value_name = 'Myeloid Progenitor Abundance', cutpoint = cp$optimal_cutpoint, ylimits = c(-2.8, 5))
Warning: cannot compute exact p-value with ties

ggsave('BALL_MultipotencyScore_Figures/BALL_vs_MPAL_bySubtype_MyeloidProg.pdf', height = 4.8, width = 15, device = 'pdf')
cp <- cutpointr(MPAL_BALL_categories, x = Pre_pDC, class = disease, na.rm = TRUE, 
                method = maximize_metric, metric = sum_sens_spec)
Assuming the positive class is MPAL
Assuming the positive class has higher x values
plot_BALL_MPAL_categories(value = 'Pre_pDC', value_name = 'Pre-pDC Abundance', cutpoint = cp$optimal_cutpoint, ylimits = c(-2.8, 4))
Warning: cannot compute exact p-value with ties

ggsave('BALL_MultipotencyScore_Figures/BALL_vs_MPAL_bySubtype_PrePDC.pdf', height = 4.8, width = 15, device = 'pdf')
cp <- cutpointr(MPAL_BALL_categories, x = Pro_B, class = disease, na.rm = TRUE, 
                method = maximize_metric, metric = sum_sens_spec)
Assuming the positive class is B-ALL
Assuming the positive class has higher x values
plot_BALL_MPAL_categories(value = 'Pro_B', value_name = 'Pro-B Abundance', cutpoint = cp$optimal_cutpoint, ylimits = c(-3.5, 3.3))
Warning: cannot compute exact p-value with ties

ggsave('BALL_MultipotencyScore_Figures/BALL_vs_MPAL_bySubtype_ProB.pdf', height = 4.8, width = 15, device = 'pdf')
cp <- cutpointr(MPAL_BALL_categories, x = Pre_B, class = disease, na.rm = TRUE, 
                method = maximize_metric, metric = sum_sens_spec)
Assuming the positive class is B-ALL
Assuming the positive class has higher x values
plot_BALL_MPAL_categories(value = 'Pre_B', value_name = 'Pre-B Abundance', cutpoint = cp$optimal_cutpoint, ylimits = c(-3, 3.8))
Warning: cannot compute exact p-value with ties

ggsave('BALL_MultipotencyScore_Figures/BALL_vs_MPAL_bySubtype_ProB.pdf', height = 4.8, width = 15, device = 'pdf')

Score on Pharmacotype Drug Screening Data

# require gene symbol column to be named "Gene"
rpkm_to_logTPM <- function(dat){
  # convert to TPM
  dat_TPM <- dat %>% 
    gather(-Gene, key = "Sample", value = "RPKM") %>%
    group_by(Sample) %>% 
    mutate(logTPM = log1p(RPKM / sum(RPKM) * 1000000)) %>% 
    select(-RPKM) %>% ungroup() %>% 
    spread(Sample, logTPM)
  
  return(dat_TPM)
}

# load pharmacotype data and convert to logTPM
pharmacotype_fpkm <- data.table::fread('../pharmacotypes/pharmacotyping_ped_rnaseq_fpkm_ALLids_0823.csv') %>% select(-GeneID) %>% dplyr::rename(Gene = GeneName)
pharmacotype_logTPM <- pharmacotype_fpkm %>% rpkm_to_logTPM()
pharmacotype_logTPM <- pharmacotype_logTPM %>% column_to_rownames('Gene') %>% data.matrix()
pharmacotype_logTPM %>% dim()
[1] 18834   712
pharmacotype_logTPM_scored <- calculate_DevState_scores(pharmacotype_logTPM, modelweights_withMultipotency, scale = T, sampleID = 'Patient ID')
[1] "Warning: 11 genes from Dev State models are missing from query dataset"
pharmacotype_logTPM_scored %>% write_csv('ALL_pharmacotypes_logTPM_DevState_scores_May2024.csv')
pharmacotype_logTPM_scored

Which version of PC1 best separates subtypes within KMT2A, DUX4, BCR::ABL1?

bulk2046_subtype_subcluster <- bulk2046 %>% filter(new_Subtype %in% c('BCR::ABL1', 'KMT2A', 'DUX4')) %>% select(Patient, PatientID = PatientID_old, new_Subtype, 
                                                                                                                Multipotency_Score, HSC_MPP, Early_Lymphoid, Pro_B, Pre_B, 
                                                                                                                Institute, oscensor, ostime, efscensor, efstime) %>% 
  left_join( read_tsv("../subtype_subcluster/Ph_sub_clusters_S2046.txt", col_names = c('PatientID', 'Class')) %>% dplyr::rename(Ph_Class = Class), by = 'PatientID') %>% 
  left_join( read_tsv("../subtype_subcluster/DUX4_bulk.txt", col_names = c('PatientID', 'Class')) %>% dplyr::rename(DUX4_Class = Class), by = 'PatientID') %>%
  left_join( read_csv("../subtype_subcluster/KMT2A_subcluster142.csv") %>% select(Patient, KMT2A_Class = Subgroup), by = 'Patient') %>% 
  arrange(new_Subtype) 

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  PatientID = col_character(),
  Class = col_character()
)
Warning: 139 parsing failures.
row col  expected    actual                                              file
  1  -- 2 columns 3 columns '../subtype_subcluster/Ph_sub_clusters_S2046.txt'
  2  -- 2 columns 3 columns '../subtype_subcluster/Ph_sub_clusters_S2046.txt'
  3  -- 2 columns 3 columns '../subtype_subcluster/Ph_sub_clusters_S2046.txt'
  4  -- 2 columns 3 columns '../subtype_subcluster/Ph_sub_clusters_S2046.txt'
  5  -- 2 columns 3 columns '../subtype_subcluster/Ph_sub_clusters_S2046.txt'
... ... ......... ......... .................................................
See problems(...) for more details.

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  PatientID = col_character(),
  Class = col_character()
)

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  Patient = col_character(),
  Subtype = col_character(),
  Subgroup = col_character(),
  Subgroup_Name = col_character()
)
bulk2046_subtype_subcluster$KMT2A_Class %>% table()
.
KMT2A-a KMT2A-b 
     17     125 
cutpointr(bulk2046_subtype_subcluster$HSC_MPP, bulk2046_subtype_subcluster$KMT2A_Class, na.rm=TRUE)
Assuming the positive class is KMT2A-b
Assuming the positive class has higher x values
cutpointr(bulk2046_subtype_subcluster$Early_Lymphoid, bulk2046_subtype_subcluster$KMT2A_Class, na.rm=TRUE)
Assuming the positive class is KMT2A-b
Assuming the positive class has higher x values
cutpointr(bulk2046_subtype_subcluster$Multipotency_Score, bulk2046_subtype_subcluster$KMT2A_Class, na.rm=TRUE)
Assuming the positive class is KMT2A-b
Assuming the positive class has higher x values
cutpointr(bulk2046_subtype_subcluster$HSC_MPP, bulk2046_subtype_subcluster$DUX4_Class, na.rm=TRUE)
Assuming the positive class is D2
Assuming the positive class has higher x values
cutpointr(bulk2046_subtype_subcluster$Early_Lymphoid, bulk2046_subtype_subcluster$DUX4_Class, na.rm=TRUE)
Assuming the positive class is D2
Assuming the positive class has higher x values
cutpointr(bulk2046_subtype_subcluster$Multipotency_Score, bulk2046_subtype_subcluster$DUX4_Class, na.rm=TRUE)
Assuming the positive class is D2
Assuming the positive class has higher x values
temp <- bulk2046_subtype_subcluster %>% filter(Ph_Class %>% str_detect("Early|Late"))

cutpointr(temp$HSC_MPP, temp$Ph_Class, na.rm=TRUE)
Assuming the positive class is Ph_Early-Pro
Assuming the positive class has higher x values
cutpointr(temp$Early_Lymphoid, temp$Ph_Class, na.rm=TRUE)
Assuming the positive class is Ph_Early-Pro
Assuming the positive class has higher x values
cutpointr(temp$Multipotency_Score, temp$Ph_Class, na.rm=TRUE)
Assuming the positive class is Ph_Early-Pro
Assuming the positive class has higher x values
temp <- bulk2046_subtype_subcluster %>% filter(Ph_Class %>% str_detect("Early|Inter"))

cutpointr(temp$HSC_MPP, temp$Ph_Class, na.rm=TRUE)
Assuming the positive class is Ph_Early-Pro
Assuming the positive class has higher x values
cutpointr(temp$Early_Lymphoid, temp$Ph_Class, na.rm=TRUE)
Assuming the positive class is Ph_Early-Pro
Assuming the positive class has higher x values
cutpointr(temp$Multipotency_Score, temp$Ph_Class, na.rm=TRUE)
Assuming the positive class is Ph_Early-Pro
Assuming the positive class has higher x values
temp <- bulk2046_subtype_subcluster %>% filter(Ph_Class %>% str_detect("Late|Inter"))

cutpointr(temp$HSC_MPP, temp$Ph_Class, na.rm=TRUE)
Assuming the positive class is Ph_Inter-Pro
Assuming the positive class has higher x values
cutpointr(temp$Early_Lymphoid, temp$Ph_Class, na.rm=TRUE)
Assuming the positive class is Ph_Inter-Pro
Assuming the positive class has higher x values
cutpointr(temp$Multipotency_Score, temp$Ph_Class, na.rm=TRUE)
Assuming the positive class is Ph_Inter-Pro
Assuming the positive class has higher x values
LS0tCnRpdGxlOiAiQi1BTEwgQ29tcG9zaXRpb24gTXVsdGlwb3RlbmN5IFNjb3JlIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KHN1cnZpdmFsKQpsaWJyYXJ5KHN1cnZtaW5lcikKbGlicmFyeShwYXRjaHdvcmspCmBgYAoKYGBge3J9CmJ1bGsyMDQ2IDwtIHJlYWRfY3N2KCdCQUxMMjA0Nl9EZXZTdGF0ZV9VcGRhdGVkX0FwcmlsMjAyNF9GdXNpb25zX01SRF9BWi5jc3YnKQpidWxrMjA0NiAKYGBgCgojIFBDQSBvbiBEZXYgU3RhdGUgQWJ1bmRhbmNlCkluY2x1ZGUgdGhlIGZvdXIgbWFpbiBsaW5lYWdlcyBhbG9uZyBCIGNlbGwgZGV2ZWxvcG1lbnQKCmBgYHtyfQpMaW5lYWdlU2NvcmVzIDwtIGJ1bGsyMDQ2ICU+JSBzZWxlY3QoUGF0aWVudElELCBIU0NfTVBQLCBFYXJseV9MeW1waG9pZCwgUHJvX0IsIFByZV9CKSAlPiUgICAKICBjb2x1bW5fdG9fcm93bmFtZXMoJ1BhdGllbnRJRCcpICU+JSBkYXRhLm1hdHJpeCgpCmJ1bGsyMDQ2JFBDMSA8LSBwcmNvbXAoTGluZWFnZVNjb3JlcylbNV0keFssMV0KcHJjb21wKExpbmVhZ2VTY29yZXMsIHNjYWxlPVQsIGNlbnRlcj1UKQpgYGAKCiMjIFByaW5jaXBhbCBDb21wb25lbnQgMSBpcyBhIE11bHRpcG90ZW5jeSBTY29yZSAKCmBgYHtyfQpEZXZTdGF0ZV9QQ0EgPC0gZGF0YS5mcmFtZShwcmNvbXAoTGluZWFnZVNjb3Jlcywgc2NhbGU9VCwgY2VudGVyPVQpWzJdJHJvdGF0aW9uKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCdEZXZTdGF0ZScpCmNvbG9yIDwtIGlmZWxzZShEZXZTdGF0ZV9QQ0EkUEMxID4gMCwgJ2RhcmtncmVlbicsICdkYXJrb3JhbmdlJykKCgpEZXZTdGF0ZV9QQ0EgJT4lIAogIG11dGF0ZShEZXZTdGF0ZSA9IGZhY3RvcihEZXZTdGF0ZSAlPiUgc3RyX3JlcGxhY2UoJ0hTQ19NUFAnLCAnSFNDL01QUCcpICU+JSBzdHJfcmVwbGFjZSgnX0InLCctQicpICU+JSBzdHJfcmVwbGFjZSgnXycsJyAnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHJldihjKCJIU0MvTVBQIiwgIkVhcmx5IEx5bXBob2lkIiwgIlByby1CIiwgIlByZS1CIikpKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IERldlN0YXRlLCB5ID0gUEMxKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBzaG93LmxlZ2VuZCA9IEZBTFNFLCBmaWxsID0gY29sb3IsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gMSwgbHdkID0gMC4yKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IERldlN0YXRlLCAjIFRleHQgd2l0aCBncm91cHMKICAgICAgICAgICAgICAgIGhqdXN0ID0gaWZlbHNlKFBDMSA8IDAsIDEuMjUsIC0wLjE1KSwKICAgICAgICAgICAgICAgIHZqdXN0ID0gMC41KSwgc2l6ZSA9IDMuNSkgKwogIHhsYWIoIkRldmVsb3BtZW50YWwgU3RhdGUiKSArIHlsYWIoIlBDMSBGZWF0dXJlIExvYWRpbmdzIikgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoLTEsIDEsIGJ5ID0gMC4yNSksIGxpbWl0cyA9IGMoLTAuOCwgMC44KSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgICMgUmVtb3ZlIFktYXhpcyB0ZXh0cwogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwgIyBSZW1vdmUgWS1heGlzIHRpY2tzCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgIyBSZW1vdmUgaG9yaXpvbnRhbCBncmlkCgpnZ3NhdmUoJ0JBTExfTXVsdGlwb3RlbmN5U2NvcmVfRmlndXJlcy9NdWx0aXBvdGVuY3lTY29yZV9QQzFfRmVhdHVyZUxvYWRpbmdzLnBkZicsIGhlaWdodCA9IDMuMiwgd2lkdGg9NikKYGBgCgojIyMgRGVyaXZlIGdlbmUgc2lnbmF0dXJlIGZvciBlc3RpbWF0aW5nIFBDMSAKCmBgYHtyfQp0cmFpbl9MQVNTTyA8LSBmdW5jdGlvbih4X3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDEpewogIAogIHRyYWluX3kgPC0geV90cmFpbiRQQzEKICAKICAjIFBlcmZvcm0gTGFzc28gcmVncmVzc2lvbiB3aXRoIExPT0NWIAogIG1vZGVsIDwtIGN2LmdsbW5ldCh4ID0geF90cmFpbiwgeSA9IHRyYWluX3ksIG5mb2xkID0gMTAsIGZhbWlseSA9ICdnYXVzc2lhbicsIGFscGhhID0gYWxwaGEsIG1heGl0PTEwMDAwMDAsIHN0YW5kYXJkaXplPUZBTFNFKQogICNwbG90KG1vZGVsKQoKICByZXR1cm4obW9kZWwpCn0KCmV2YWx1YXRlX21vZGVsIDwtIGZ1bmN0aW9uKG1vZGVsLCB4X3ZhbCwgYW5ub192YWwsIGxhbWJkYSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVfbmFtZSwgaXRlcmF0aW9uLCBmb2xkbmFtZSl7CgogICMgQ3JlYXRlIHNjb3JlIGNsYXNzaWZpY2F0aW9uIHdpdGggc3Vydml2YWwgYW5kIGdldCBjb3ZhcmlhdGVzCiAgcHJlZF95IDwtIHByZWRpY3QobW9kZWwsIHhfdmFsLCBzID0gbGFtYmRhKSAlPiUgZGF0YS5mcmFtZSgpCiAgY29sbmFtZXMocHJlZF95KSA8LSAnUHJlZFNjb3JlJwogIHByZWRfeSA8LSBwcmVkX3kgJT4lIHJvd25hbWVzX3RvX2NvbHVtbignUGF0aWVudCcpICU+JSAKICAgICMgYWRkIGFubm8gdG8gZ2V0IGNvdmFyaWF0ZXMKICAgIGxlZnRfam9pbihhbm5vX3ZhbCwgYnkgPSAnUGF0aWVudCcpCiAgCiAgIyBDYWxjdWxhdGUgY29ycmVsYXRpb24gaW4gdmFsaWRhdGlvbiBzZXQKICBwZWFyc29uIDwtIGNvcihwcmVkX3kkUHJlZFNjb3JlLCBwcmVkX3kkUEMxLCBtZXRob2QgPSAncGVhcnNvbicpCiAgc3BlYXJtYW4gPC0gY29yKHByZWRfeSRQcmVkU2NvcmUsIHByZWRfeSRQQzEsIG1ldGhvZCA9ICdzcGVhcm1hbicpCiAgCiAgIyBTdW1tYXJ5IE1ldHJpY3MKICBzdW1tYXJ5X21ldHJpY3MgPC0gZGF0YS5mcmFtZSgKICAgICdtb2RlbF9pZCcgPSBwYXN0ZTAoZmVhdHVyZV9uYW1lLCAnX2l0ZXInLCBpdGVyYXRpb24sICdfJywgZm9sZG5hbWUpLAogICAgJ2xhbWJkYScgPSBsYW1iZGEsCiAgICAnbW9kZWxfc2l6ZScgPSBzdW0oY29lZihtb2RlbCwgcyA9IGxhbWJkYSkhPTApLAogICAgJ3BlYXJzb24nID0gcGVhcnNvbiwKICAgICdzcGVhcm1hbicgPSBzcGVhcm1hbiwKICAgICdmZWF0dXJlcycgPSBmZWF0dXJlX25hbWUsCiAgICAnaXRlcmF0aW9uJyA9IGl0ZXJhdGlvbiwKICAgICdmb2xkbmFtZScgPSBmb2xkbmFtZQogICkKICByZXR1cm4oc3VtbWFyeV9tZXRyaWNzKQp9CgoKZ3JpZHNlYXJjaF9sYXNzbyA8LSBmdW5jdGlvbihleHByX3RyYWluLCBleHByX3ZhbCwgYW5ub190cmFpbiwgYW5ub192YWwsIGZlYXR1cmVzLCBmZWF0dXJlX25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlcmF0aW9uLCBmb2xkbmFtZSwgc3VtbWFyeV9tZXRyaWNzKXsKICAKICAjIEZpbHRlciBleHByIG1hdHJpeCBmb3IgZmVhdHVyZSBzZXQKICB4X3RyYWluIDwtIGV4cHJfdHJhaW5bLCBjb2xuYW1lcyhleHByX3RyYWluKSAlaW4lIGZlYXR1cmVzXQogIHhfdmFsIDwtIGV4cHJfdmFsWywgY29sbmFtZXMoZXhwcl92YWwpICVpbiUgZmVhdHVyZXNdCgogICMgVHJhaW4gTEFTU08gCiAgbW9kZWwgPC0gdHJhaW5fTEFTU08oeF90cmFpbiwgYW5ub190cmFpbikKCiAgIyBHZXQgc3VtbWFyeSBtZXRyaWNzIGZvciBsYW1iZGEubWluIGFuZCBsYW1iZGEuMXNlCiAgZm9yKGxhbWJkYSBpbiBjKCdsYW1iZGEubWluJywgJ2xhbWJkYS4xc2UnKSl7CiAgICBzdW1tYXJ5X21ldHJpY3MgPC0gc3VtbWFyeV9tZXRyaWNzICU+JSByYmluZCgKICAgICAgZXZhbHVhdGVfbW9kZWwobW9kZWwgPSBtb2RlbCwgeF92YWwgPSB4X3ZhbCwgYW5ub192YWwgPSBhbm5vX3ZhbCwgbGFtYmRhID0gbGFtYmRhLCAKICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZV9uYW1lID0gZmVhdHVyZV9uYW1lLCBpdGVyYXRpb24gPSBpdGVyYXRpb24sIGZvbGRuYW1lID0gZm9sZG5hbWUpKQogIH0KICAKICByZXR1cm4oc3VtbWFyeV9tZXRyaWNzKQp9CgoKbmVzdGVkQ1ZfcmVncmVzc2lvbiA8LSBmdW5jdGlvbih0cmFpbl9hbm5vLCB0cmFpbl9leHByLCBpdGVyYXRpb24sIGZlYXR1cmVfc2V0cywgc3VtbWFyeV9tZXRyaWNzKXsKICAjIHNldCB1cCByYW5kb20gc2VlZCBhbmQgc2h1ZmZsZSBkYXRhIAogIHNldC5zZWVkKGl0ZXJhdGlvbikKICB0cmFpbl9hbm5vIDwtIHRyYWluX2Fubm9bc2FtcGxlKG5yb3codHJhaW5fYW5ubykpLF0KICB0cmFpbl9leHByIDwtIHRyYWluX2V4cHJbc2FtcGxlKG5yb3codHJhaW5fZXhwcikpLF0KICAKICAjIyAxMC1mb2xkIG91dGVyIGNyb3NzIHZhbGlkYXRpb24KICBmb2xkcyA8LSByc2FtcGxlOjp2Zm9sZF9jdih0cmFpbl9hbm5vLCAxMCkKICBmb3Iob3V0ZXJfY3YgaW4gMToxMCl7CiAgICAjIGZvbGQgSUQKICAgIGZvbGRuYW1lIDwtIGZvbGRzJGlkW1tvdXRlcl9jdl1dCiAgICAjIGdldCBhbm5vIHNwbGl0cwogICAgYW5ub190cmFpbiA8LSBhbmFseXNpcyhmb2xkcyRzcGxpdHNbW291dGVyX2N2XV0pCiAgICBhbm5vX3ZhbCA8LSBhc3Nlc3NtZW50KGZvbGRzJHNwbGl0c1tbb3V0ZXJfY3ZdXSkKICAgICMgZ2V0IGV4cHIgc3BsaXRzCiAgICBleHByX3RyYWluIDwtIHRyYWluX2V4cHJbYW5ub190cmFpbiRQYXRpZW50LF0KICAgIGV4cHJfdmFsIDwtIHRyYWluX2V4cHJbYW5ub192YWwkUGF0aWVudCxdCiAgICAKICAgICMgSXRlcmF0ZSB0aHJvdWdoIGZlYXR1cmUgc2V0IGFuZCBydW4gZ3JpZHNlYXJjaCB0byB0cmFpbiBzdXJ2aXZhbCBmdW5jdGlvbnMKICAgIGZvcihmZWF0dXJlX25hbWUgaW4gbmFtZXMoZmVhdHVyZV9zZXRzKSl7CiAgICAgICMgZ2V0IGZlYXR1cmUgbGlzdAogICAgICBmZWF0dXJlcyA8LSBmZWF0dXJlX3NldHNbW2ZlYXR1cmVfbmFtZV1dCiAgICAgICMgcnVuIGdyaWRzZWFyY2ggYW5kIGdldCByZXN1bHRzCiAgICAgIHN1bW1hcnlfbWV0cmljcyA8LSBncmlkc2VhcmNoX2xhc3NvKGV4cHJfdHJhaW4gPSBleHByX3RyYWluLCBleHByX3ZhbCA9IGV4cHJfdmFsLCBhbm5vX3RyYWluID0gYW5ub190cmFpbiwgYW5ub192YWwgPSBhbm5vX3ZhbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGZlYXR1cmVzLCBmZWF0dXJlX25hbWUgPSBmZWF0dXJlX25hbWUsIGl0ZXJhdGlvbiA9IGl0ZXJhdGlvbiwgZm9sZG5hbWUgPSBmb2xkbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcnlfbWV0cmljcyA9IHN1bW1hcnlfbWV0cmljcykKICAgIH0KICB9CiAgcmV0dXJuKHN1bW1hcnlfbWV0cmljcykKfQoKYGBgCgpgYGB7cn0KYnVsazIwNDZfdnN0IDwtIHJlYWRSRFMoJy4uL0JBTEwyMDQ2X0J1bGtSTkFfdnN0LnJkcycpCmJ1bGsyMDQ2X3ZzdCAlPiUgZGltKCkKYGBgCgpgYGB7cn0KIyBUcmFpbiBmcm9tIGdlbmVzIHVzZWQgdG8gcHJlZGljdCB0aGUgZm91ciBiZGV2IGxpbmVhZ2UgcG9wdWxhdGlvbnMKbW9kZWx3ZWlnaHRzIDwtIHJlYWRfY3N2KCIuLi9OTUZfTGFzc29fTW9kZWxXZWlnaHRzLmNzdiIpICU+JSBzZWxlY3QoR2VuZSwgSFNDX01QUCwgRWFybHlfTHltcGhvaWQsIFByb19CLCBQcmVfQikgJT4lIAogIHJvd3dpc2UoKSAlPiUgbXV0YXRlKHN1bXdlaWdodHMgPSBzdW0oSFNDX01QUCwgRWFybHlfTHltcGhvaWQsIFByb19CLCBQcmVfQikpICU+JSBmaWx0ZXIoc3Vtd2VpZ2h0cyAhPSAwKSAlPiUgc2VsZWN0KC1zdW13ZWlnaHRzKSAKCmJ1bGsyMDQ2X3ZzdF9maWx0ZXJlZCA8LSBidWxrMjA0Nl92c3RbbW9kZWx3ZWlnaHRzJEdlbmUsXSAKYnVsazIwNDZfdnN0X2ZpbHRlcmVkICU+JSBkaW0oKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHRpZHltb2RlbHMpCmxpYnJhcnkoZ2xtbmV0KQoKQ1ZvdXRwdXQgPC0gZGF0YS5mcmFtZSgpCnRyYWluX3ggPC0gYnVsazIwNDZfdnN0X2ZpbHRlcmVkWyxidWxrMjA0NiRTYW1wbGVJRF9vbGRdICU+JSB0KCkKdHJhaW5feSA8LSBidWxrMjA0NiAlPiUgc2VsZWN0KFBhdGllbnQgPSBTYW1wbGVJRF9vbGQsIFBDMSkjICU+JSBtdXRhdGUoUEMxID0gLVBDMSkKZmVhdHVyZXNwYWNlIDwtIGxpc3QoJ01vZGVsV2VpZ2h0czExNScgPSBtb2RlbHdlaWdodHMkR2VuZSkKdGVtcF9vdXRwdXQgPC0gZGF0YS5mcmFtZSgpCgoKZm9yKGl0ZXJhdGlvbiBpbiAxOjEwKXsKICBwcmludChwYXN0ZTAoJ2l0ZXJhdGlvbiAnLCBpdGVyYXRpb24pKQogIENWb3V0cHV0IDwtIG5lc3RlZENWX3JlZ3Jlc3Npb24odHJhaW5fYW5ubyA9IHRyYWluX3ksIHRyYWluX2V4cHIgPSB0cmFpbl94LCBpdGVyYXRpb24gPSBpdGVyYXRpb24sIGZlYXR1cmVfc2V0cyA9IGZlYXR1cmVzcGFjZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyeV9tZXRyaWNzID0gQ1ZvdXRwdXQpIAp9CiMjIGFubm90YXRlIGFuZCBhZGQgdG8gZmluYWwgb3V0cHV0CkNWb3V0cHV0IApDVm91dHB1dCAlPiUgd3JpdGVfY3N2KCdSZXBOZXN0ZWRDVl9yZXN1bHRzX1BDMW11bHRpcG90ZW5jeV9SZWdyZXNzaW9uLmNzdicpCmBgYAoKYGBge3J9CnRyYWluX0xBU1NPIDwtIGZ1bmN0aW9uKHhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMSl7CiAgCiAgdHJhaW5feSA8LSB5X3RyYWluJFBDMQogIAogICMgUGVyZm9ybSBMYXNzbyByZWdyZXNzaW9uIHdpdGggTE9PQ1YgCiAgbW9kZWwgPC0gY3YuZ2xtbmV0KHggPSB4X3RyYWluLCB5ID0gdHJhaW5feSwgbmZvbGQgPSAxMCwgZmFtaWx5ID0gJ2dhdXNzaWFuJywgYWxwaGEgPSBhbHBoYSwgbWF4aXQ9MTAwMDAwMCwgc3RhbmRhcmRpemU9RkFMU0UpCiAgI3Bsb3QobW9kZWwpCgogIHJldHVybihtb2RlbCkKfQpgYGAKCgpgYGB7cn0KbW9kZWwgPC0gdHJhaW5fTEFTU08odHJhaW5feCwgeV90cmFpbiA9IHRyYWluX3kpCgojIFBDMSBtb2RlbCB3ZWlnaHRzClBDMV9tb2RlbHdlaWdodHMgPC0gZGF0YS5mcmFtZSgpCgpQQzFfbW9kZWx3ZWlnaHRzIDwtIG1vZGVsICU+JSBjb2VmKHMgPSAnbGFtYmRhLjFzZScpICU+JSBkYXRhLm1hdHJpeCgpICU+JSAKICAgICAgZGF0YS5mcmFtZSgpICU+JSBkcGx5cjo6cmVuYW1lKFdlaWdodCA9IHMxKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCdHZW5lJykgJT4lIAogICAgICB0YWlsKC0xKSAlPiUgZmlsdGVyKFdlaWdodCAhPSAwKSAlPiUgYXJyYW5nZSgtV2VpZ2h0KSAKICAgIApQQzFfbW9kZWx3ZWlnaHRzIDwtIFBDMV9tb2RlbHdlaWdodHMgJT4lIHNlbGVjdChHZW5lLCBXZWlnaHQpClBDMV9tb2RlbHdlaWdodHMKYGBgCgpgYGB7cn0KIyBDcmVhdGUgZmluYWwgbW9kZWwgbWF0cml4Cm1vZGVsd2VpZ2h0c193aXRoTXVsdGlwb3RlbmN5IDwtIHJlYWRfY3N2KCIuLi9OTUZfTGFzc29fTW9kZWxXZWlnaHRzLmNzdiIpICAlPiUgCiAgbGVmdF9qb2luKFBDMV9tb2RlbHdlaWdodHMgJT4lIHNlbGVjdChHZW5lLCBNdWx0aXBvdGVuY3lfU2NvcmUgPSBXZWlnaHQpKSAlPiUgCiAgcmVwbGFjZShpcy5uYSguKSwgMCkgCgptb2RlbHdlaWdodHNfd2l0aE11bHRpcG90ZW5jeSAlPiUgd3JpdGVfY3N2KCJEZXZTdGF0ZV9MYXNzb19Nb2RlbFdlaWdodHNfd2l0aE11bHRpcG90ZW5jeVNjb3JlX01heTIwMjQuY3N2IikKYGBgCgoKYGBge3J9CmNhbGN1bGF0ZV9EZXZTdGF0ZV9zY29yZXMgPSBmdW5jdGlvbihxdWVyeSwgbW9kZWx3ZWlnaHRzLCBzY2FsZSA9IFRSVUUsIHNhbXBsZUlEID0gJ1BhdGllbnQnKXsKICAKICAjIENoZWNrIGZvciBvdmVybGFwIHdpdGggbW9kZWwgZ2VuZXMgYW5kIHF1ZXJ5IGdlbmVzCiAgcXVlcnlnZW5lcyA8LSByb3duYW1lcyhxdWVyeSkKICBtb2RlbHdlaWdodHNfbWlzc2luZyA8LSBzdW0oIShtb2RlbHdlaWdodHMkR2VuZSAlaW4lIHF1ZXJ5Z2VuZXMpKQogICMgY2hlY2sgZm9yIG1pc3NpbmcgZ2VuZXMKICBpZihtb2RlbHdlaWdodHNfbWlzc2luZyA+IDApewogICAgcHJpbnQocGFzdGUwKCdXYXJuaW5nOiAnLCBtb2RlbHdlaWdodHNfbWlzc2luZywgJyBnZW5lcyBmcm9tIERldiBTdGF0ZSBtb2RlbHMgYXJlIG1pc3NpbmcgZnJvbSBxdWVyeSBkYXRhc2V0JykpCiAgfQogIAogICMgZmlsdGVyIG1vZGVsIHdlaWdodHMKICBtb2RlbHdlaWdodHMgPC0gbW9kZWx3ZWlnaHRzICU+JSBmaWx0ZXIoR2VuZSAlaW4lIHF1ZXJ5Z2VuZXMpCiAgbW9kZWx3ZWlnaHRzX21hdCA8LSBtb2RlbHdlaWdodHMgJT4lIGNvbHVtbl90b19yb3duYW1lcygnR2VuZScpICU+JSBkYXRhLm1hdHJpeCgpCiAgCiAgIyBtdWx0aXBseSBxdWVyeSBieSBEZXYgU3RhdGUgbGFzc28gd2VpZ2h0cwogIHNjb3JlZCA8LSAodChxdWVyeVttb2RlbHdlaWdodHMkR2VuZSxdKSAlKiUgbW9kZWx3ZWlnaHRzX21hdCkgJT4lIGRhdGEubWF0cml4KCkgCiAgaWYoc2NhbGUgPT0gVFJVRSl7CiAgICBzY29yZWQgPC0gc2NhbGUoc2NvcmVkKQogIH0KICBzY29yZWQgPC0gc2NvcmVkICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbihzYW1wbGVJRCkgCiAgCiAgcmV0dXJuKHNjb3JlZCkKfQpgYGAKCgojIyBDYWxjdWxhdGUgaW4gYnVsazIwNDYgYW5kIHZhbGlkYXRlCgpgYGB7cn0KYnVsazIwNDYgPC0gYnVsazIwNDYgJT4lIAogIGxlZnRfam9pbiggY2FsY3VsYXRlX0RldlN0YXRlX3Njb3JlcyhidWxrMjA0Nl92c3QsIG1vZGVsd2VpZ2h0c193aXRoTXVsdGlwb3RlbmN5LCBzY2FsZSA9IFQsIHNhbXBsZUlEID0gJ1NhbXBsZUlEX29sZCcpICU+JSBzZWxlY3QoU2FtcGxlSURfb2xkLCBNdWx0aXBvdGVuY3lfU2NvcmUpICkgCmJ1bGsyMDQ2CmBgYAoKYGBge3J9CiMgTG9hZCByZXBlYXRlZCBuZXN0ZWQgY3Jvc3MtdmFsaWRhdGlvbiAoMTAtZm9sZCwgMTAgcmVwZWF0cykgcmVzdWx0cyAKQ1ZvdXRwdXQgPC0gcmVhZF9jc3YoJ1JlcE5lc3RlZENWX3Jlc3VsdHNfUEMxbXVsdGlwb3RlbmN5X1JlZ3Jlc3Npb24uY3N2JykKUEMxbW9kZWxfQ1ZwbG90IDwtIENWb3V0cHV0ICU+JSBmaWx0ZXIobGFtYmRhID09ICdsYW1iZGEuMXNlJykgJT4lCiAgbXV0YXRlKG1vZGVsID0gJycpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBtb2RlbCwgeSA9IHBlYXJzb25eMikpICsKICB5bGFiKCdQZWFyc29uIENvcnJlbGF0aW9uIHdpdGggUEMxJykgKyB4bGFiKCdQQzEgTW9kZWxzXG4oQ3Jvc3MtVmFsaWRhdGlvbiknKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLnNpemU9MCkgKyBnZ2JlZXN3YXJtOjpnZW9tX3F1YXNpcmFuZG9tKHdpZHRoPTAuMzIpICsgdGhlbWVfcHVicigpICsgdGhlbWUoYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpKQoKIyBHZXQgY29ycmVsYXRpb24gYWNyb3NzIGZ1bGwgY29ob3J0CmJ1bGsyMDQ2X1BDMV9NdWx0aXBvdGVuY3lfcGxvdCA8LSBidWxrMjA0NiAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gTXVsdGlwb3RlbmN5X1Njb3JlLCB5ID0gUEMxKSkgKyAKICB4bGFiKCdCLUFMTCBNdWx0aXBvdGVuY3kgU2NvcmUgKDk5IEdlbmVzKScpICsgCiAgZ2VvbV9wb2ludChzaXplPTAuNykgKyBzdGF0X2NvcihyLmRpZ2l0cyA9IDQpICsgdGhlbWVfcHVicigpCgpQQzFtb2RlbF9DVnBsb3QgKyBidWxrMjA0Nl9QQzFfTXVsdGlwb3RlbmN5X3Bsb3QgKyBwbG90X2xheW91dCh3aWR0aHM9YygwLjI1LDAuNzUpKQpnZ3NhdmUoJ0JBTExfTXVsdGlwb3RlbmN5U2NvcmVfRmlndXJlcy9NdWx0aXBvdGVuY3lTY29yZV9QQzFfTW9kZWxQZXJmb3JtYW5jZS5wZGYnLCBoZWlnaHQgPSA0LjUsIHdpZHRoPTgpCmBgYAoKCmBgYHtyfQpidWxrMjA0NiAlPiUgd3JpdGVfY3N2KCdCQUxMMjA0Nl9EZXZTdGF0ZV9VcGRhdGVkX01heTIwMjRfQVouY3N2JykKYnVsazIwNDYKYGBgCgoKIyBNdWx0aXBvdGVuY3kgU2NvcmUgaW4gTm9ybWFsIEIgY2VsbCBEZXZlbG9wbWVudAoKYGBge3J9CmxpYnJhcnkoREVTZXEyKQpCRGV2X3BzZXVkb2J1bGsgPC0gcmVhZFJEUygnLi4vLi4vQkRldmVsb3BtZW50X1BzZXVkb2J1bGtfYnlUaXNzdWVfQ2VsbFR5cGUucmRzJykKIyB2c3Qgbm9ybWFsaXplCkJEZXZfcHNldWRvYnVsa1tbJ1JOQSddXUBkYXRhIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoQkRldl9wc2V1ZG9idWxrW1snUk5BJ11dQGNvdW50cywgY29sRGF0YSA9IEJEZXZfcHNldWRvYnVsa0BtZXRhLmRhdGEsIGRlc2lnbiA9IH4xKSAlPiUgdnN0KCkgJT4lIGFzc2F5KCkKQkRldl9wc2V1ZG9idWxrW1snUk5BJ11dQGRhdGFbMToxMCwxOjVdCmBgYAoKYGBge3J9CiMgQ2FsY3VsYXRlIE11bHRpcG90ZW5jeSBzY29yZQpCRGV2X3BzZXVkb2J1bGtAbWV0YS5kYXRhIDwtIGJpbmRfY29scyhCRGV2X3BzZXVkb2J1bGtAbWV0YS5kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FsY3VsYXRlX0RldlN0YXRlX3Njb3JlcyhCRGV2X3BzZXVkb2J1bGtbWydSTkEnXV1AZGF0YSwgbW9kZWx3ZWlnaHRzX3dpdGhNdWx0aXBvdGVuY3ksIHNjYWxlID0gVCwgc2FtcGxlSUQgPSAnU2FtcGxlJykgJT4lIGNvbHVtbl90b19yb3duYW1lcygnU2FtcGxlJykpCkJEZXZfcHNldWRvYnVsa0BtZXRhLmRhdGEKYGBgCgpgYGB7cn0KQkRldl9wc2V1ZG9idWxrQG1ldGEuZGF0YSAlPiUgCiAgZmlsdGVyKG5DZWxscyA+PSA1MCkgJT4lIAogIG11dGF0ZShCRGV2ZWxvcG1lbnRfQ2VsbFR5cGVfQ29tcHJlaGVuc2l2ZSA9IEJEZXZlbG9wbWVudF9DZWxsVHlwZV9Db21wcmVoZW5zaXZlICU+JSAKICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnSFNDL01QUCcsICdNUFAtTXlMeScsICdMTVBQJywgJ0Vhcmx5IEdNUCcsICdQcmUtcERDJywgJ1ByZS1wREMgQ3ljbGluZycsICdwREMnLCAnTUxQJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0NMUCcsICdQcmUtUHJvLUInLCAnUHJvLUIgVkRKJywgJ1Byby1CIEN5Y2xpbmcgMScsICdQcm8tQiBDeWNsaW5nIDInLCAgIyAnUHJlLVByby1CIEN5Y2xpbmcnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnTGFyZ2UgUHJlLUIgMScsICdMYXJnZSBQcmUtQiAyJywgJ1NtYWxsIFByZS1CJywgJ0ltbWF0dXJlIEInLCAnTWF0dXJlIEInKSkpICU+JSAgI01hdHVyZSBCIEN5Y2xpbmcKICBtdXRhdGUoYERldmVsb3BtZW50YWwgU3RhdGVgID0gaWZlbHNlKEJEZXZlbG9wbWVudF9DZWxsVHlwZV9Db21wcmVoZW5zaXZlICVpbiUgYygnSFNDL01QUCcsICdNUFAtTXlMeScsICdMTVBQJyksICdIU0MvTVBQJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShCRGV2ZWxvcG1lbnRfQ2VsbFR5cGVfQ29tcHJlaGVuc2l2ZSAlaW4lIGMoJ0Vhcmx5IEdNUCcpLCAnTXllbG9pZCBQcm9nZW5pdG9yJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoQkRldmVsb3BtZW50X0NlbGxUeXBlX0NvbXByZWhlbnNpdmUgJWluJSBjKCdQcmUtcERDJywgJ1ByZS1wREMgQ3ljbGluZycsICdwREMnKSwgJ1ByZS1wREMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoQkRldmVsb3BtZW50X0NlbGxUeXBlX0NvbXByZWhlbnNpdmUgJWluJSBjKCdNTFAnLCAnQ0xQJywgJ1ByZS1Qcm8tQicsICdQcmUtUHJvLUIgQ3ljbGluZycpLCAnRWFybHkgTHltcGhvaWQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEJEZXZlbG9wbWVudF9DZWxsVHlwZV9Db21wcmVoZW5zaXZlICVpbiUgYygnUHJvLUIgVkRKJywgJ1Byby1CIEN5Y2xpbmcgMScsICdQcm8tQiBDeWNsaW5nIDInKSwgJ1Byby1CJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoQkRldmVsb3BtZW50X0NlbGxUeXBlX0NvbXByZWhlbnNpdmUgJWluJSBjKCdMYXJnZSBQcmUtQiAxJywgJ0xhcmdlIFByZS1CIDInLCAnU21hbGwgUHJlLUInKSwgJ1ByZS1CJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEJEZXZlbG9wbWVudF9DZWxsVHlwZV9Db21wcmVoZW5zaXZlICVpbiUgYygnSW1tYXR1cmUgQicsICdNYXR1cmUgQicsICdNYXR1cmUgQiBDeWNsaW5nJyksICdNYXR1cmUgQicsICdOQScpKSkpKSkpICU+JSAKICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnSFNDL01QUCcsICdNeWVsb2lkIFByb2dlbml0b3InLCAnUHJlLXBEQycsICdFYXJseSBMeW1waG9pZCcsICdQcm8tQicsICdQcmUtQicsICdNYXR1cmUgQicpKSkgJT4lIAogIGZpbHRlcighaXMubmEoQkRldmVsb3BtZW50X0NlbGxUeXBlX0NvbXByZWhlbnNpdmUpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gQkRldmVsb3BtZW50X0NlbGxUeXBlX0NvbXByZWhlbnNpdmUsIHkgPSBNdWx0aXBvdGVuY3lfU2NvcmUsIGZpbGwgPSBgRGV2ZWxvcG1lbnRhbCBTdGF0ZWApKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBsdHk9NSwgc2l6ZT0wLjUsIGFscGhhPTAuOCkgKyBnZW9tX2JveHBsb3Qob3V0bGllci5zaXplPTAuMykgKyAKICB0aGVtZV9wdWJyKGxlZ2VuZD0ncmlnaHQnKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xLCBzaXplPTEwLjUpKSArIAogIHhsYWIoJ1xuTm9ybWFsIFBvcHVsYXRpb24gKEIgQ2VsbCBEZXZlbG9wbWVudCBBdGxhcyknKSArIHlsYWIoJ0ItQUxMIE11bHRpcG90ZW5jeSBTY29yZScpICsgCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICdEYXJrMicpCgpnZ3NhdmUoJ0JBTExfTXVsdGlwb3RlbmN5U2NvcmVfRmlndXJlcy9NdWx0aXBvdGVuY3lTY29yZV9Ob3JtYWxfQkRldl9wc2V1ZG9idWxrU2NvcmVzLnBkZicsIGhlaWdodCA9IDUsIHdpZHRoPTkuNSkKYGBgCgojIEV2YWx1YXRlIDk5LWdlbmUgTXVsdGlwb3RlbmN5IFNjb3JlIHdpdGhpbiAyMDQ2IGNvaG9ydCAKCmBgYHtyfQpidWxrMjA0NiA8LSByZWFkX2NzdignQkFMTDIwNDZfRGV2U3RhdGVfVXBkYXRlZF9NYXkyMDI0X0FaLmNzdicpCmBgYAoKIyMgRXZhbHVhdGUgb24gQ2xpbmljYWwgUmlzayBHcm91cCAKCmBgYHtyfQpidWxrMjA0NiRSaXNrX0dyb3VwICU+JSB0YWJsZSgpCmBgYAoKCmBgYHtyfQpwIDwtIGJ1bGsyMDQ2ICU+JSAKICAgIHNlbGVjdChQYXRpZW50LCBSaXNrX0dyb3VwLCBNdWx0aXBvdGVuY3lfU2NvcmUpICU+JSBwaXZvdF9sb25nZXIoLWMoUGF0aWVudCwgUmlza19Hcm91cCksIG5hbWVzX3RvPSdMaW5lYWdlJywgdmFsdWVzX3RvPSdTY29yZScpICU+JQogICAgZmlsdGVyKFJpc2tfR3JvdXAgIT0gJ05BJykgJT4lIG11dGF0ZShSaXNrX0dyb3VwID0gUmlza19Hcm91cCAlPiUgZmFjdG9yKGxldmVscyA9IGMoJ0NoaWxkaG9vZCBTUicsICdDaGlsZGhvb2QgSFInLCAnQVlBJywgJ0FkdWx0JykpKSAlPiUgCiAgICBmaWx0ZXIoTGluZWFnZSAlaW4lIGMoJ011bHRpcG90ZW5jeV9TY29yZScpKSAlPiUgCiAgICBtdXRhdGUoTGluZWFnZSA9IExpbmVhZ2UgJT4lIHN0cl9yZXBsYWNlKCdfJywgJyAnKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gUmlza19Hcm91cCwgeSA9IFNjb3JlLCBmaWxsID0gUmlza19Hcm91cCkpICsgCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBhbHBoYSA9IDAuNywgbHR5ID0gMikgKyAKICAgIGdlb21fYm94cGxvdChvdXRsaWVyLnNpemU9MCkgKyBnZ2JlZXN3YXJtOjpnZW9tX3F1YXNpcmFuZG9tKGFlcyhzaXplID0gUmlza19Hcm91cCksIHdpZHRoPTAuMykgKyAKICAgIGZhY2V0X3dyYXAoLn5MaW5lYWdlLCBzY2FsZXM9J2ZyZWUnLCBuY29sPTUpICsgCiAgICB0aGVtZV9wdWJyKGxlZ2VuZCA9ICdub25lJykgKyAKICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAnRGFyazInKSArIAogICAgdGhlbWUoc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSksIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLjUpKSArIAogICAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gYygwLjIsIDAuMiwgMC4zLCAwLjMpKSArIHhsYWIoJ1xuQ2xpbmljYWwgUmlzayBHcm91cCcpICsgeWxhYignQi1BTEwgTXVsdGlwb3RlbmN5IFNjb3JlJykgKyAKICAgIHN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IGxpc3QoYygnQ2hpbGRob29kIFNSJywgJ0NoaWxkaG9vZCBIUicpLCBjKCdDaGlsZGhvb2QgSFInLCAnQVlBJyksIGMoJ0FZQScsICdBZHVsdCcpKSkKCnAKZ2dzYXZlKCdCQUxMX011bHRpcG90ZW5jeVNjb3JlX0ZpZ3VyZXMvUmlza0dyb3VwXzIwMjJwYXRpZW50c19NdWx0aXBvdGVuY3lfU2NvcmUucGRmJywgZGV2aWNlID0gJ3BkZicsIGhlaWdodCA9IDUuNSwgd2lkdGggPSA0LjgpCmBgYAoKYGBge3J9CiMgUGF0aWVudHMgd2l0aCBBZ2UgZGF0YQpzdW0oIWlzLm5hKGJ1bGsyMDQ2JEFnZSkpIApgYGAKCgpgYGB7cn0KcCA8LSBidWxrMjA0NiAlPiUgc2VsZWN0KEFnZSwgTXVsdGlwb3RlbmN5X1Njb3JlKSAlPiUgcGl2b3RfbG9uZ2VyKC1BZ2UpICU+JSAKICAgIG11dGF0ZShgRGV2ZWxvcG1lbnRhbCBTdGF0ZWAgPSBuYW1lICU+JSBzdHJfcmVwbGFjZSgnXycsJyAnKSkgJT4lCiAgICAjbXV0YXRlKGBEZXZlbG9wbWVudGFsIFN0YXRlYCA9IGlmZWxzZShuYW1lICU+JSBzdHJfZGV0ZWN0KCdFYXJseScpLCAnRWFybHkgTHltcGhvaWQnLAogICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UobmFtZSAlPiUgc3RyX2RldGVjdCgnUHJvX0InKSwgJ1Byby1CJywgJ05BJykpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBBZ2UsIHkgPSB2YWx1ZSwgY29sb3IgPSBgRGV2ZWxvcG1lbnRhbCBTdGF0ZWApKSArIAogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xvZXNzJywgc2UgPSBULCBzaXplID0gMikgKyAjZ2VvbV9wb2ludChzaXplID0gMC4zLCBhbHBoYSA9IDAuNSkgKyAKICAgIHNjYWxlX3hfc3FydChicmVha3MgPSBjKDEsIDUsIDEwLCAxNSwgMjAsIDMwLCA0MCwgNTAsIDYwLCA3MCwgODApKSArIAogICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAnRGFyazInKSArIAogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gMykgKyAKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDEsIGx0eSA9IDMpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMTUsIGx0eSA9IDMpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNDAsIGx0eSA9IDMpICsgCiAgICB4bGFiKCdBZ2UgYXQgRGlhZ25vc2lzIChZZWFycyknKSArIHlsYWIoJ0ItQUxMIE11bHRpcG90ZW5jeSBTY29yZScpICsKICAgIGdncHVicjo6dGhlbWVfcHVicihsZWdlbmQgPSAndG9wJykgKyB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgICAgICAgCnAKZ2dzYXZlKCdCQUxMX011bHRpcG90ZW5jeVNjb3JlX0ZpZ3VyZXMvQWdlX3ZzX011bHRpcG90ZW5jeVNjb3JlXzIwMTlwYXRpZW50cy5wZGYnLCBoZWlnaHQgPSA0LjIsIHdpZHRoID0gNS41KQpgYGAKCgpgYGB7cn0KcCA8LSBidWxrMjA0NiAlPiUgc2VsZWN0KEFnZSwgTXVsdGlwb3RlbmN5X1Njb3JlKSAlPiUgcGl2b3RfbG9uZ2VyKC1BZ2UpICU+JSAKICAgIG11dGF0ZShgRGV2ZWxvcG1lbnRhbCBTdGF0ZWAgPSBuYW1lICU+JSBzdHJfcmVwbGFjZSgnXycsJyAnKSkgJT4lCiAgICAjbXV0YXRlKGBEZXZlbG9wbWVudGFsIFN0YXRlYCA9IGlmZWxzZShuYW1lICU+JSBzdHJfZGV0ZWN0KCdFYXJseScpLCAnRWFybHkgTHltcGhvaWQnLAogICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UobmFtZSAlPiUgc3RyX2RldGVjdCgnUHJvX0InKSwgJ1Byby1CJywgJ05BJykpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBBZ2UsIHkgPSB2YWx1ZSwgY29sb3IgPSBgRGV2ZWxvcG1lbnRhbCBTdGF0ZWApKSArIAogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xvZXNzJywgc2UgPSBULCBzaXplID0gMikgKyAjZ2VvbV9wb2ludChzaXplID0gMC4zLCBhbHBoYSA9IDAuNSkgKyAKICAgIHNjYWxlX3hfc3FydChicmVha3MgPSBjKDEsIDUsIDEwLCAxNSwgMjAsIDMwLCA0MCwgNTAsIDYwLCA3MCwgODApKSArIAogICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAnRGFyazInKSArIAogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gMykgKyAKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDEsIGx0eSA9IDMpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMTgsIGx0eSA9IDMpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNDAsIGx0eSA9IDMpICsgCiAgICB4bGFiKCdBZ2UgYXQgRGlhZ25vc2lzIChZZWFycyknKSArIHlsYWIoJ0ItQUxMIE11bHRpcG90ZW5jeSBTY29yZScpICsKICAgIGdncHVicjo6dGhlbWVfcHVicihsZWdlbmQgPSAndG9wJykgKyB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgICAgICAgCnAKI2dnc2F2ZSgnQkFMTF9NdWx0aXBvdGVuY3lTY29yZV9GaWd1cmVzL0FnZV92c19NdWx0aXBvdGVuY3lTY29yZV8yMDE5cGF0aWVudHMucGRmJywgaGVpZ2h0ID0gNC4yLCB3aWR0aCA9IDUuNSkKYGBgCgoKIyMgRXZhbHVhdGUgb24gTVJEIAoKYGBge3J9CiMgRm9ybWF0dGVkIGFuZCBwaXZvdGVkCmJ1bGsyMDQ2X01SRCA8LSBidWxrMjA0NiAlPiUgCiAgc2VsZWN0KFBhdGllbnQsIE1SRF9EMjlfMmNhdCwgTVJEX0QyOV8zY2F0LCBNUkRfRDI5XzVjYXQsICBNUkRfRDQ2XzJjYXQsIAogICAgICAgICBjKCdIU0NfTVBQJywgJ0Vhcmx5X0x5bXBob2lkJywgJ015ZWxvaWRfUHJvZycsICdQcmVfcERDJywgJ1Byb19CJywgJ1ByZV9CJywgJ01hdHVyZV9CJywgJ1RfTksnLCAnTW9ub2N5dGUnLCAnRXJ5dGhyb2lkJywKICAgICAgICAgICAnUEMxJywgJ011bHRpcG90ZW5jeV9TY29yZScpKSAlPiUgCiAgbXV0YXRlKE1SRF9EMjlfMmNhdCA9IGZhY3RvcihNUkRfRDI5XzJjYXQsIGxldmVscyA9IGMoJ05lZ2F0aXZlXG48IDAuMDElJywgJ1Bvc2l0aXZlXG4+IDAuMDElJykpLCAKICAgICAgICAgTVJEX0QyOV8zY2F0ID0gZmFjdG9yKE1SRF9EMjlfM2NhdCwgbGV2ZWxzID0gYygnPCAwLjAxJScsICcwLjAxIC0gMSUnLCAgJz4gMSUnKSksIAogICAgICAgICBNUkRfRDI5XzVjYXQgPSBmYWN0b3IoTVJEX0QyOV81Y2F0LCBsZXZlbHMgPSBjKCc8IDAuMDElJywgJzAuMDEgLSAwLjElJywgJzAuMSAtIDElJywgJzEgLSAxMCUnLCAnPiAxMCUnKSkpICU+JSAKICBwaXZvdF9sb25nZXIoLWMoUGF0aWVudCwgTVJEX0QyOV8yY2F0LCBNUkRfRDI5XzNjYXQsIE1SRF9EMjlfNWNhdCwgIE1SRF9ENDZfMmNhdCksIG5hbWVzX3RvID0gJ0xpbmVhZ2UnLCB2YWx1ZXNfdG8gPSAnU2NvcmUnKSAlPiUgCiAgc2VsZWN0KFBhdGllbnQsIExpbmVhZ2UsIFNjb3JlLCBldmVyeXRoaW5nKCkpICU+JSAKICBtdXRhdGUoTGluZWFnZSA9IExpbmVhZ2UgJT4lICNzdHJfcmVwbGFjZSgnTk1GLipfJywgJycpICU+JSAKICAgICAgICAgICAgICAgIGZhY3RvcihsZXZlbHMgPSBjKCdNdWx0aXBvdGVuY3lfU2NvcmUnLCAnUEMxJywgJ0hTQ19NUFAnLCAnRWFybHlfTHltcGhvaWQnLCAnTXllbG9pZF9Qcm9nJywgJ1ByZV9wREMnLCAnUHJvX0InLCAnUHJlX0InLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdNYXR1cmVfQicsICdUX05LJywgJ01vbm9jeXRlJywgJ0VyeXRocm9pZCcpKSkgJT4lIAogIGFycmFuZ2UoTGluZWFnZSkKCmJ1bGsyMDQ2X01SRApgYGAKCmBgYHtyfQpwIDwtIGJ1bGsyMDQ2X01SRCAlPiUgCiAgICBmaWx0ZXIoTVJEX0QyOV81Y2F0ICE9ICdOQScpICU+JSAjbXV0YXRlKFNjb3JlID0gaWZlbHNlKExpbmVhZ2UgJWluJSBjKCdQQzFfYScsICdQQzFfYicsICdQQzFfYycpLCAtU2NvcmUsIFNjb3JlKSkgJT4lIAogICAgZmlsdGVyKExpbmVhZ2UgJWluJSBjKCdNdWx0aXBvdGVuY3lfU2NvcmUnKSkgJT4lIAogICAgbXV0YXRlKExpbmVhZ2UgPSBMaW5lYWdlICU+JSBzdHJfcmVwbGFjZSgnXycsICcgJykpICU+JSAKICAgIGdncGxvdChhZXMoeCA9IE1SRF9EMjlfNWNhdCwgeSA9IFNjb3JlLCBmaWxsID0gTVJEX0QyOV81Y2F0KSkgKyAKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGFscGhhID0gMC43LCBsdHkgPSAyKSArIAogICAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2l6ZT0wKSArIGdnYmVlc3dhcm06Omdlb21fcXVhc2lyYW5kb20oYWVzKHNpemUgPSBNUkRfRDI5XzVjYXQpLCB3aWR0aD0wLjMpICsgCiAgICBmYWNldF93cmFwKC5+TGluZWFnZSwgc2NhbGVzPSdmcmVlJywgbmNvbD01KSArIAogICAgdGhlbWVfcHVicihsZWdlbmQgPSAnbm9uZScpICsgCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCcjN0NBOTg3JywgJyNEOUQxN0QnLCAnI0RBQTE1RScsICcjRkY3RjUwJywgJyNENDNGMDAnKSkgKyAKICAgIHRoZW1lKHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMi41KSkgKyAKICAgIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IGMoMC4xNSwgMC40LCAwLjQsIDAuNSwgMC41KSkgKyB4bGFiKCdcblJlc2lkdWFsIERpc2Vhc2UgLSBEYXkgMjkgSW5kdWN0aW9uJykgKyB5bGFiKCdCLUFMTCBNdWx0aXBvdGVuY3kgU2NvcmUnKSArIAogICAgc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbGlzdChjKCc8IDAuMDElJywgJzAuMDEgLSAwLjElJyksIGMoJzwgMC4wMSUnLCAnMC4xIC0gMSUnKSwgYygnPCAwLjAxJScsICcxIC0gMTAlJyksIGMoJzwgMC4wMSUnLCAnPiAxMCUnKSkpCgpwCmdnc2F2ZSgnQkFMTF9NdWx0aXBvdGVuY3lTY29yZV9GaWd1cmVzL01SRF9sZXZlbHNfNzk0cGF0aWVudHNfTXVsdGlwb3RlbmN5X1Njb3JlLnBkZicsIGRldmljZSA9ICdwZGYnLCBoZWlnaHQgPSA1LjUsIHdpZHRoID0gNC44KQpgYGAKCgpgYGB7cn0KcCA8LSBidWxrMjA0Nl9NUkQgJT4lIAogICAgZmlsdGVyKE1SRF9EMjlfMmNhdCAhPSAnTkEnKSAlPiUgCiAgICBmaWx0ZXIoTGluZWFnZSAlaW4lIGMoJ011bHRpcG90ZW5jeV9TY29yZScpKSAlPiUgCiAgICBtdXRhdGUoTGluZWFnZSA9IExpbmVhZ2UgJT4lIHN0cl9yZXBsYWNlKCdfJywgJyAnKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gTVJEX0QyOV8yY2F0LCB5ID0gU2NvcmUsIGZpbGwgPSBNUkRfRDI5XzJjYXQpKSArIAogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgYWxwaGEgPSAwLjcsIGx0eSA9IDIpICsgCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaXplPTApICsgZ2diZWVzd2FybTo6Z2VvbV9xdWFzaXJhbmRvbShzaXplID0gMC4yNSwgd2lkdGg9MC4zKSArIAogICAgZmFjZXRfd3JhcCgufkxpbmVhZ2UsIHNjYWxlcz0nZnJlZScsIG5jb2w9NSkgKyAKICAgIHRoZW1lX3B1YnIobGVnZW5kID0gJ25vbmUnKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygnIzdDQTk4NycsICcjRDk3QTZEJykpICsgCiAgICB0aGVtZShzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzLjUpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMi41KSkgKyAKICAgIHhsYWIoJ1xuUmVzaWR1YWwgRGlzZWFzZSAtIERheSAyOSBJbmR1Y3Rpb24nKSArIHlsYWIoJ0ItQUxMIE11bHRpcG90ZW5jeSBTY29yZScpICsgCiAgICBzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBsaXN0KGMoJ05lZ2F0aXZlXG48IDAuMDElJywgJ1Bvc2l0aXZlXG4+IDAuMDElJykpKQoKcApnZ3NhdmUoJ0JBTExfTXVsdGlwb3RlbmN5U2NvcmVfRmlndXJlcy9NUkRfU3RhdHVzXzExOTdwYXRpZW50c19NdWx0aXBvdGVuY3lfU2NvcmUucGRmJywgZGV2aWNlID0gJ3BkZicsIGhlaWdodCA9IDUsIHdpZHRoID0gNCkKYGBgCgoKIyMgRXZhbHVhdGUgb24gU3Vydml2YWwgT3V0Y29tZXMgCgojIyMgUGVkaWF0cmljIFN1cnZpdmFsIAoKRmlyc3QgaW4gUGVkaWF0cmljIEItQUxMIApTdXJ2aXZhbCBvdXRjb21lcyBhcmUgYXZhaWxhYmxlIGZvciAxLDAzOSBwZWRpYXRyaWMgQi1BTEwgcGF0aWVudHMgd2l0aGluIHRoZSBTdCBKdWRlIGFuZCBDT0cgY29ob3J0cy4KICBOb3RlIHRoYXQgdGhlIENPRyBwYXRpZW50IHNhbXBsZXMgc3ViamVjdCBmb3IgUk5BLXNlcSB3ZXJlIHByZXNlbGVjdGVkIHRvIGJlIGhpZ2ggcmlzayBhbmQgY29uc2VxdWVudGlhbGx5IHRoZXNlIENPRyBvdXRjb21lcyBhcmUgd29yc2UgdGhhbiB0aGUgInJlYWwgd29ybGQiCgpgYGB7cn0KIyBLZWVwIHBlZGlhdHJpYyBjb2hvcnRzIChDT0cgYW5kIFN0IEp1ZGUpCmJ1bGsyMDQ2X3BlZGlhdHJpYyA8LSBidWxrMjA0NiAlPiUgZmlsdGVyKEluc3RpdHV0ZSAlaW4lIGMoJ1N0IEp1ZGUnLCAnQ09HJykpICU+JQogICAgZmlsdGVyKCFpcy5uYShvc2NlbnNvcikgJiAhaXMubmEoZWZzY2Vuc29yKSkgJT4lIAogICAgbXV0YXRlKFJpc2tfR3JvdXAgPSBmYWN0b3IoUmlza19Hcm91cCwgbGV2ZWxzID0gYygnQ2hpbGRob29kIFNSJywgJ0NoaWxkaG9vZCBIUicsICdBWUEnKSksICAjIGNsaW5pY2FsIHJpc2sgZ3JvdXAKICAgICAgICAgICBHZW5vbWljUmlza19wZWRpYXRyaWMgPSBmYWN0b3IoR2Vub21pY1Jpc2tfcGVkaWF0cmljLCBsZXZlbHMgPSBjKCdVbmNsYXNzaWZpZWQnLCAnRmF2b3JhYmxlJywgJ0ludGVybWVkaWF0ZScsICdVbmZhdm9yYWJsZScpKSkgICAgICMgZ2Vub21pYyByaXNrIGdyb3VwCgpidWxrMjA0Nl9wZWRpYXRyaWMKYGBgCgpMb29wIHRocm91Z2ggZGV2ZWxvcG1lbnRhbCBzdGF0ZXMgYW5kIHBlcmZvcm0gdW5pdmFyaWFibGUgKyBtdWx0aXZhcmlhYmxlIGNveCByZWdyZXNzaW9uCgpgYGB7cn0KIyBkYXRhZnJhbWUgdG8gc3RvcmUgcmVzdWx0cwpsaW5lYWdlX3N1cnZpdmFsX3BlZCA8LSBkYXRhLmZyYW1lKCkKZGV2c3RhdGVzIDwtIGMoJ011bHRpcG90ZW5jeV9TY29yZScsICdIU0NfTVBQJywgJ015ZWxvaWRfUHJvZycsICdQcmVfcERDJywgJ0Vhcmx5X0x5bXBob2lkJywgJ1Byb19CJywgJ1ByZV9CJywgJ01hdHVyZV9CJywgJ1RfTksnLCAnTW9ub2N5dGUnLCAnRXJ5dGhyb2lkJykKCmZvcihsaW5lYWdlIGluIGRldnN0YXRlcyl7CgogICAgIyBVbml2YXJpYWJsZSBtb2RlbCAKICAgIG1vZF9vcyA8LSBjb3hwaChTdXJ2KG9zdGltZSwgb3NjZW5zb3IpIH4gZ2V0KGxpbmVhZ2UpLCBidWxrMjA0Nl9wZWRpYXRyaWMpCiAgICBtb2RfZWZzIDwtIGNveHBoKFN1cnYoZWZzdGltZSwgZWZzY2Vuc29yKSB+IGdldChsaW5lYWdlKSwgYnVsazIwNDZfcGVkaWF0cmljKQoKICAgIGxpbmVhZ2Vfc3Vydml2YWxfcGVkIDwtIGxpbmVhZ2Vfc3Vydml2YWxfcGVkICU+JSBiaW5kX3Jvd3MoCiAgICAgICAgc3VtbWFyeShtb2Rfb3MpJGNvZWZmaWNpZW50cyAlPiUgZGF0YS5mcmFtZSgpICU+JSBkcGx5cjo6cmVuYW1lKEhSID0gJ2V4cC5jb2VmLicsIEhSc2UgPSAnc2UuY29lZi4nLCBzdGF0aXN0aWMgPSAneicsIHB2YWx1ZSA9ICdQci4uLnouLicpICU+JSAKICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCd2YXJpYWJsZScpICU+JSBtdXRhdGUoTGluZWFnZSA9IGxpbmVhZ2UsIG11bHRpdmFyaWFibGUgPSBGQUxTRSwgc3Vydml2YWwgPSAnT1MnKSAlPiUgCiAgICAgICAgICAgIG11dGF0ZShIUl91cHIgPSBIUiArIDEuOTYqSFJzZSwgSFJfbHdyID0gSFIgLSAxLjk2KkhSc2UsIAogICAgICAgICAgICAgICAgICAgbl9wYXRpZW50cyA9IG1vZF9vcyRuKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcih2YXJpYWJsZSAlPiUgc3RyX2RldGVjdCgnbGluZWFnZScpKSAlPiUgc2VsZWN0KExpbmVhZ2UsIHN1cnZpdmFsLCBtdWx0aXZhcmlhYmxlLCBIUiwgSFJzZSwgSFJfbHdyLCBIUl91cHIsIG5fcGF0aWVudHMsIHN0YXRpc3RpYywgcHZhbHVlKQogICAgKSAlPiUgYmluZF9yb3dzKAogICAgICAgIHN1bW1hcnkobW9kX2VmcykkY29lZmZpY2llbnRzICU+JSBkYXRhLmZyYW1lKCkgJT4lIGRwbHlyOjpyZW5hbWUoSFIgPSAnZXhwLmNvZWYuJywgSFJzZSA9ICdzZS5jb2VmLicsIHN0YXRpc3RpYyA9ICd6JywgcHZhbHVlID0gJ1ByLi4uei4uJykgJT4lIAogICAgICAgICAgICByb3duYW1lc190b19jb2x1bW4oJ3ZhcmlhYmxlJykgJT4lIG11dGF0ZShMaW5lYWdlID0gbGluZWFnZSwgbXVsdGl2YXJpYWJsZSA9IEZBTFNFLCBzdXJ2aXZhbCA9ICdFRlMnKSAlPiUgCiAgICAgICAgICAgIG11dGF0ZShIUl91cHIgPSBIUiArIDEuOTYqSFJzZSwgSFJfbHdyID0gSFIgLSAxLjk2KkhSc2UsIAogICAgICAgICAgICAgICAgICAgbl9wYXRpZW50cyA9IG1vZF9lZnMkbikgJT4lIAogICAgICAgICAgICBmaWx0ZXIodmFyaWFibGUgJT4lIHN0cl9kZXRlY3QoJ2xpbmVhZ2UnKSkgJT4lIHNlbGVjdChMaW5lYWdlLCBzdXJ2aXZhbCwgbXVsdGl2YXJpYWJsZSwgSFIsIEhSc2UsIEhSX2x3ciwgSFJfdXByLCBuX3BhdGllbnRzLCBzdGF0aXN0aWMsIHB2YWx1ZSkKICAgICkKICAgIAogICAgIyBNdWx0aXZhcmlhYmxlIG1vZGVsIAogICAgbW9kX211bHRpX29zIDwtIGNveHBoKFN1cnYob3N0aW1lLCBvc2NlbnNvcikgfiBnZXQobGluZWFnZSkgKyBSaXNrX0dyb3VwICsgR2Vub21pY1Jpc2tfcGVkaWF0cmljICsgU2V4ICsgQWdlICsgV0JDLCBidWxrMjA0Nl9wZWRpYXRyaWMpCiAgICBtb2RfbXVsdGlfZWZzIDwtIGNveHBoKFN1cnYoZWZzdGltZSwgZWZzY2Vuc29yKSB+IGdldChsaW5lYWdlKSArIFJpc2tfR3JvdXAgKyBHZW5vbWljUmlza19wZWRpYXRyaWMgKyBTZXggKyBBZ2UgKyBXQkMsIGJ1bGsyMDQ2X3BlZGlhdHJpYykKICAgIAogICAgIyBDcmVhdGUgYmFzZWxpbmUgbW9kZWxzIHdpdGhvdXQgZGV2IHN0YXRlIGFidW5kYW5jZSB0byBjb21wYXJlIGFnYWluc3QgYnkgbmVzdGVkIExSVAogICAgbW9kX211bHRpX29zXzAgPC0gY294cGgoU3Vydihvc3RpbWUsIG9zY2Vuc29yKSB+IFJpc2tfR3JvdXAgKyBHZW5vbWljUmlza19wZWRpYXRyaWMgKyBTZXggKyBBZ2UgKyBXQkMsIGJ1bGsyMDQ2X3BlZGlhdHJpYykKICAgIG1vZF9tdWx0aV9lZnNfMCA8LSBjb3hwaChTdXJ2KGVmc3RpbWUsIGVmc2NlbnNvcikgfiBSaXNrX0dyb3VwICsgR2Vub21pY1Jpc2tfcGVkaWF0cmljICsgU2V4ICsgQWdlICsgV0JDICwgYnVsazIwNDZfcGVkaWF0cmljKQoKICAgIGxpbmVhZ2Vfc3Vydml2YWxfcGVkIDwtIGxpbmVhZ2Vfc3Vydml2YWxfcGVkICU+JSBiaW5kX3Jvd3MoCiAgICAgICAgc3VtbWFyeShtb2RfbXVsdGlfb3MpJGNvZWZmaWNpZW50cyAlPiUgZGF0YS5mcmFtZSgpICU+JSBkcGx5cjo6cmVuYW1lKEhSID0gJ2V4cC5jb2VmLicsIEhSc2UgPSAnc2UuY29lZi4nLCBzdGF0aXN0aWMgPSAneicsIHB2YWx1ZSA9ICdQci4uLnouLicpICU+JSAKICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCd2YXJpYWJsZScpICU+JSBtdXRhdGUoTGluZWFnZSA9IGxpbmVhZ2UsIG11bHRpdmFyaWFibGUgPSBUUlVFLCBzdXJ2aXZhbCA9ICdPUycpICU+JSAKICAgICAgICAgICAgbXV0YXRlKEhSX3VwciA9IEhSICsgMS45NipIUnNlLCBIUl9sd3IgPSBIUiAtIDEuOTYqSFJzZSwgCiAgICAgICAgICAgICAgICAgICBuX3BhdGllbnRzID0gbW9kX211bHRpX29zJG4pICU+JSAKICAgICAgICAgICAgZmlsdGVyKHZhcmlhYmxlICU+JSBzdHJfZGV0ZWN0KCdsaW5lYWdlJykpICU+JSBzZWxlY3QoTGluZWFnZSwgc3Vydml2YWwsIG11bHRpdmFyaWFibGUsIEhSLCBIUnNlLCBIUl9sd3IsIEhSX3Vwciwgbl9wYXRpZW50cywgc3RhdGlzdGljLCBwdmFsdWUpICU+JSAKICAgICAgICAgICAgIyBuZXN0ZWQgTFJUOiBob3cgbXVjaCBwcm9nbm9zdGljIGluZm8gZG9lcyBkZXYgc3RhdGUgYWRkIGJleW9uZCBiYXNlbGluZSBtb2RlbD8KICAgICAgICAgICAgbXV0YXRlKHB2YWx1ZV9uZXN0ZWRMUlQgPSBhbm92YShtb2RfbXVsdGlfb3MsIG1vZF9tdWx0aV9vc18wKVsyLCdQKD58Q2hpfCknXSkKICAgICAgICAKICAgICkgJT4lIGJpbmRfcm93cygKICAgICAgCiAgICAgICAgc3VtbWFyeShtb2RfbXVsdGlfZWZzKSRjb2VmZmljaWVudHMgJT4lIGRhdGEuZnJhbWUoKSAlPiUgZHBseXI6OnJlbmFtZShIUiA9ICdleHAuY29lZi4nLCBIUnNlID0gJ3NlLmNvZWYuJywgc3RhdGlzdGljID0gJ3onLCBwdmFsdWUgPSAnUHIuLi56Li4nKSAlPiUgCiAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbigndmFyaWFibGUnKSAlPiUgbXV0YXRlKExpbmVhZ2UgPSBsaW5lYWdlLCBtdWx0aXZhcmlhYmxlID0gVFJVRSwgc3Vydml2YWwgPSAnRUZTJykgJT4lIAogICAgICAgICAgICBtdXRhdGUoSFJfdXByID0gSFIgKyAxLjk2KkhSc2UsIEhSX2x3ciA9IEhSIC0gMS45NipIUnNlLCAKICAgICAgICAgICAgICAgICAgIG5fcGF0aWVudHMgPSBtb2RfbXVsdGlfZWZzJG4pICU+JQogICAgICAgICAgICBmaWx0ZXIodmFyaWFibGUgJT4lIHN0cl9kZXRlY3QoJ2xpbmVhZ2UnKSkgJT4lIHNlbGVjdChMaW5lYWdlLCBzdXJ2aXZhbCwgbXVsdGl2YXJpYWJsZSwgSFIsIEhSc2UsIEhSX2x3ciwgSFJfdXByLCBuX3BhdGllbnRzLCBzdGF0aXN0aWMsIHB2YWx1ZSkgJT4lIAogICAgICAgICAgICAjIG5lc3RlZCBMUlQ6IGhvdyBtdWNoIHByb2dub3N0aWMgaW5mbyBkb2VzIGRldiBzdGF0ZSBhZGQgYmV5b25kIGJhc2VsaW5lIG1vZGVsPwogICAgICAgICAgICBtdXRhdGUocHZhbHVlX25lc3RlZExSVCA9IGFub3ZhKG1vZF9tdWx0aV9lZnMsIG1vZF9tdWx0aV9lZnNfMClbMiwnUCg+fENoaXwpJ10pCiAgICAgICAgCiAgICApCn0KCmxpbmVhZ2Vfc3Vydml2YWxfcGVkIApgYGAKCgpgYGB7ciwgZmlnLmhlaWdodCA9IDUsIGZpZy53aWR0aCA9IDZ9CnN1cnZfcGVkX3VuaSA8LSBsaW5lYWdlX3N1cnZpdmFsX3BlZCAlPiUgbXV0YXRlKGxvZ3B2YWwgPSBpZmVsc2UoSFIgPiAxLCAtbG9nMTAocHZhbHVlKSwgbG9nMTAocHZhbHVlKSkpICU+JSAKICAgIG11dGF0ZShBc3NvY2lhdGlvbiA9IGlmZWxzZShIUl9sd3IgPiAxICYgcHZhbHVlIDwgMC4wNSwgJ0FkdmVyc2UnLCBpZmVsc2UoSFJfdXByIDwgMSAmIHB2YWx1ZSA8IDAuMDUsICdGYXZvcmFibGUnLCAnTi5TLicpKSkgJT4lCiAgICBmaWx0ZXIobXVsdGl2YXJpYWJsZSA9PSBGQUxTRSkgJT4lIAogICAgbXV0YXRlKHN1cnZuYW1lID0gaWZlbHNlKHN1cnZpdmFsID09ICdPUycsICdPdmVyYWxsIFN1cnZpdmFsJywgaWZlbHNlKHN1cnZpdmFsID09ICdFRlMnLCAnRXZlbnQtRnJlZSBTdXJ2aXZhbCcsICdOQScpKSAlPiUgCiAgICAgICAgICAgZmFjdG9yKGxldmVscyA9IGMoJ092ZXJhbGwgU3Vydml2YWwnLCAnRXZlbnQtRnJlZSBTdXJ2aXZhbCcpKSkgJT4lCiAgICBtdXRhdGUoTGluZWFnZSA9IGlmZWxzZShMaW5lYWdlICVpbiUgYygnTXllbG9pZF9Qcm9nJywgJ0Vhcmx5X0x5bXBob2lkJywgJ01hdHVyZV9CJywgJ011bHRpcG90ZW5jeV9TY29yZScpLCBMaW5lYWdlICU+JSBzdHJfcmVwbGFjZSgnUHJvZycsICdQcm9nZW5pdG9yJykgJT4lIHN0cl9yZXBsYWNlKCdfJywgJyAnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBMaW5lYWdlICU+JSBzdHJfcmVwbGFjZSgnX01QUCcsICcvTVBQJykgJT4lIHN0cl9yZXBsYWNlKCdfTksnLCAnL05LJykgJT4lIHN0cl9yZXBsYWNlKCdfJywgJy0nKSkgJT4lIAogICAgICAgICAgICAgZmFjdG9yKGxldmVscyA9IGMoJ011bHRpcG90ZW5jeSBTY29yZScsICdIU0MvTVBQJywgJ015ZWxvaWQgUHJvZ2VuaXRvcicsICdQcmUtcERDJywgJ0Vhcmx5IEx5bXBob2lkJywgJ1Byby1CJywgJ1ByZS1CJykpKSAlPiUgZmlsdGVyKExpbmVhZ2UgIT0gJ05BJykgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBMaW5lYWdlLCB5ID0gSFIsIHltYXggPSBIUl91cHIsIHltaW4gPSBIUl9sd3IsIGNvbG9yID0gQXNzb2NpYXRpb24pKSArIAogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbHR5ID0gMiwgc2l6ZSA9IDAuNSwgYWxwaGEgPSAxLCBjb2xvciA9ICdkYXJrZ3JleScpICsgCiAgICBnZW9tX3BvaW50cmFuZ2Uoc2l6ZT0wLjgsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPWMoMCkpKSArCiAgICBmYWNldF93cmFwKC5+c3Vydm5hbWUsIG5jb2w9MSwgc2NhbGVzID0gJ2ZyZWVfeCcpICsgCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ2luZGlhbnJlZDQnLCAnZGFya2dyZWVuJywgJyM2NjY2NjYnKSkgKyAgIAogICAgdGhlbWVfcHVicihsZWdlbmQgPSAncmlnaHQnKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3Mobj04KSwgbGltaXRzID0gYygwLjMsMS44KSkgKyB0aGVtZShheGlzLnRpdGxlLng9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICAgIHlsYWIoJ1VuaXZhcmlhYmxlIEhhemFyZCBSYXRpbyAgKHBlciAxU0QgaW5jcmVhc2UgaW4gYWJ1bmRhbmNlKScpICsgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAuNSwgYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTExKSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMC41KSwKICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMC41KSkgKyAKICAgIGdndGl0bGUoJ1VuaXZhcmlhYmxlIFN1cnZpdmFsIChuID0gMTAzOSknKQogICAgCnN1cnZfcGVkX211bHRpIDwtIGxpbmVhZ2Vfc3Vydml2YWxfcGVkICU+JSBtdXRhdGUobG9ncHZhbCA9IGlmZWxzZShIUiA+IDEsIC1sb2cxMChwdmFsdWUpLCBsb2cxMChwdmFsdWUpKSkgJT4lIAogICAgbXV0YXRlKEFzc29jaWF0aW9uID0gaWZlbHNlKEhSX2x3ciA+IDEgJiBwdmFsdWVfbmVzdGVkTFJUIDwgMC4wNSwgJ0FkdmVyc2UnLCBpZmVsc2UoSFJfdXByIDwgMSAmIHB2YWx1ZV9uZXN0ZWRMUlQgPCAwLjA1LCAnRmF2b3JhYmxlJywgJ04uUy4nKSkpICU+JQogICAgZmlsdGVyKG11bHRpdmFyaWFibGUgPT0gVFJVRSkgJT4lIAogICAgbXV0YXRlKHN1cnZuYW1lID0gaWZlbHNlKHN1cnZpdmFsID09ICdPUycsICdPdmVyYWxsIFN1cnZpdmFsJywgaWZlbHNlKHN1cnZpdmFsID09ICdFRlMnLCAnRXZlbnQtRnJlZSBTdXJ2aXZhbCcsICdOQScpKSAlPiUgCiAgICAgICAgICAgZmFjdG9yKGxldmVscyA9IGMoJ092ZXJhbGwgU3Vydml2YWwnLCAnRXZlbnQtRnJlZSBTdXJ2aXZhbCcpKSkgJT4lCiAgICBtdXRhdGUoTGluZWFnZSA9IGlmZWxzZShMaW5lYWdlICVpbiUgYygnTXllbG9pZF9Qcm9nJywgJ0Vhcmx5X0x5bXBob2lkJywgJ01hdHVyZV9CJywgJ011bHRpcG90ZW5jeV9TY29yZScpLCBMaW5lYWdlICU+JSBzdHJfcmVwbGFjZSgnUHJvZycsICdQcm9nZW5pdG9yJykgJT4lIHN0cl9yZXBsYWNlKCdfJywgJyAnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBMaW5lYWdlICU+JSBzdHJfcmVwbGFjZSgnX01QUCcsICcvTVBQJykgJT4lIHN0cl9yZXBsYWNlKCdfTksnLCAnL05LJykgJT4lIHN0cl9yZXBsYWNlKCdfJywgJy0nKSkgJT4lIAogICAgICAgICAgICAgZmFjdG9yKGxldmVscyA9IGMoJ011bHRpcG90ZW5jeSBTY29yZScsJ0hTQy9NUFAnLCAnTXllbG9pZCBQcm9nZW5pdG9yJywgJ1ByZS1wREMnLCAnRWFybHkgTHltcGhvaWQnLCAnUHJvLUInLCAnUHJlLUInKSkpICU+JSBmaWx0ZXIoTGluZWFnZSAhPSAnTkEnKSAlPiUKICAgIGdncGxvdChhZXMoeCA9IExpbmVhZ2UsIHkgPSBIUiwgeW1heCA9IEhSX3VwciwgeW1pbiA9IEhSX2x3ciwgY29sb3IgPSBBc3NvY2lhdGlvbikpICsgCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsdHkgPSAyLCBzaXplID0gMC41LCBhbHBoYSA9IDEsIGNvbG9yID0gJ2RhcmtncmV5JykgKyAKICAgIGdlb21fcG9pbnRyYW5nZShzaXplPTAuOCwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9YygwKSkpICsKICAgIGZhY2V0X3dyYXAoLn5zdXJ2bmFtZSwgbmNvbD0xLCBzY2FsZXMgPSAnZnJlZV94JykgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnaW5kaWFucmVkNCcsICdkYXJrZ3JlZW4nLCAnIzY2NjY2NicpKSArICAgCiAgICB0aGVtZV9wdWJyKGxlZ2VuZCA9ICdyaWdodCcpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNjYWxlczo6cHJldHR5X2JyZWFrcyhuPTgpLCBsaW1pdHMgPSBjKDAuMywxLjgpKSArIHRoZW1lKGF4aXMudGl0bGUueD0gZWxlbWVudF9ibGFuaygpKSArIAogICAgeWxhYignTXVsdGl2YXJpYWJsZSBIYXphcmQgUmF0aW8gIChwZXIgMVNEIGluY3JlYXNlIGluIGFidW5kYW5jZSknKSArIAogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLjUsIGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMSksIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTAuNSksCiAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAuNSkpICsgCiAgICBnZ3RpdGxlKCdNdWx0aXZhcmlhYmxlIFN1cnZpdmFsIChuID0gMTAxMCknKQogICAgCmdnc2F2ZShwbG90ID0gc3Vydl9wZWRfdW5pLCBmaWxlbmFtZSA9ICdCQUxMX3N1cnZpdmFsX2ZpZ3VyZXNfZmluYWwvU3Vydml2YWxfcGVkaWF0cmljX1VuaXZhcmlhYmxlX0JEZXZPbmx5X0hhemFyZFJhdGlvcy5wZGYnLCBkZXZpY2UgPSAncGRmJywgaGVpZ2h0ID0gNy41LCB3aWR0aCA9IDQuNSkKZ2dzYXZlKHBsb3QgPSBzdXJ2X3BlZF9tdWx0aSwgZmlsZW5hbWUgPSAnQkFMTF9zdXJ2aXZhbF9maWd1cmVzX2ZpbmFsL1N1cnZpdmFsX3BlZGlhdHJpY19NdWx0aXZhcmlhYmxlX0JEZXZPbmx5X0hhemFyZFJhdGlvcy5wZGYnLCBkZXZpY2UgPSAncGRmJywgaGVpZ2h0ID0gNy41LCB3aWR0aCA9IDQuNSkKCnN1cnZfcGVkX3VuaSB8IHN1cnZfcGVkX211bHRpCmBgYAoKYGBge3J9CmJ1bGsyMDQ2X3BlZGlhdHJpYyRNUkRfRDI5XzJjYXQgJT4lIHRhYmxlKCkKYGBgCgoKIyMjIFBlZGlhdHJpYyBTdXJ2aXZhbDsgTVJEIG5lZ2F0aXZlIG9ubHkKUmVwZWF0IFBlZGlhdHJpYyBBbmFseXNpcyB3aXRoaW4gTVJEIG5lZ2F0aXZlIHBhdGllbnRzIAoKYGBge3J9CmJ1bGsyMDQ2X3BlZGlhdHJpY19NUkRuZWcgPC0gYnVsazIwNDZfcGVkaWF0cmljICU+JSBmaWx0ZXIoTVJEX0QyOV8yY2F0ID09ICdOZWdhdGl2ZVxuPCAwLjAxJScpCgojIGRhdGFmcmFtZSB0byBzdG9yZSByZXN1bHRzCmxpbmVhZ2Vfc3Vydml2YWxfcGVkX01SRG5lZyA8LSBkYXRhLmZyYW1lKCkKZGV2c3RhdGVzIDwtIGMoJ011bHRpcG90ZW5jeV9TY29yZScsICdIU0NfTVBQJywgJ015ZWxvaWRfUHJvZycsICdQcmVfcERDJywgJ0Vhcmx5X0x5bXBob2lkJywgJ1Byb19CJywgJ1ByZV9CJywgJ01hdHVyZV9CJywgJ1RfTksnLCAnTW9ub2N5dGUnLCAnRXJ5dGhyb2lkJykKCmZvcihsaW5lYWdlIGluIGRldnN0YXRlcyl7CgogICAgIyBNdWx0aXZhcmlhYmxlIG1vZGVsIAogICAgbW9kX211bHRpX29zIDwtIGNveHBoKFN1cnYob3N0aW1lLCBvc2NlbnNvcikgfiBnZXQobGluZWFnZSkgKyBSaXNrX0dyb3VwICsgR2Vub21pY1Jpc2tfcGVkaWF0cmljICsgU2V4ICsgQWdlICsgV0JDLCBidWxrMjA0Nl9wZWRpYXRyaWNfTVJEbmVnKQogICAgbW9kX211bHRpX2VmcyA8LSBjb3hwaChTdXJ2KGVmc3RpbWUsIGVmc2NlbnNvcikgfiBnZXQobGluZWFnZSkgKyBSaXNrX0dyb3VwICsgR2Vub21pY1Jpc2tfcGVkaWF0cmljICsgU2V4ICsgQWdlICsgV0JDICwgYnVsazIwNDZfcGVkaWF0cmljX01SRG5lZykKICAgIAogICAgIyBDcmVhdGUgYmFzZWxpbmUgbW9kZWxzIHdpdGhvdXQgZGV2IHN0YXRlIGFidW5kYW5jZSB0byBjb21wYXJlIGFnYWluc3QgYnkgbmVzdGVkIExSVAogICAgbW9kX211bHRpX29zXzAgPC0gY294cGgoU3Vydihvc3RpbWUsIG9zY2Vuc29yKSB+IFJpc2tfR3JvdXAgKyBHZW5vbWljUmlza19wZWRpYXRyaWMgKyBTZXggKyBBZ2UgKyBXQkMsIGJ1bGsyMDQ2X3BlZGlhdHJpY19NUkRuZWcpCiAgICBtb2RfbXVsdGlfZWZzXzAgPC0gY294cGgoU3VydihlZnN0aW1lLCBlZnNjZW5zb3IpIH4gUmlza19Hcm91cCArIEdlbm9taWNSaXNrX3BlZGlhdHJpYyArIFNleCArIEFnZSArIFdCQyAsIGJ1bGsyMDQ2X3BlZGlhdHJpY19NUkRuZWcpCgogICAgbGluZWFnZV9zdXJ2aXZhbF9wZWRfTVJEbmVnIDwtIGxpbmVhZ2Vfc3Vydml2YWxfcGVkX01SRG5lZyAlPiUgYmluZF9yb3dzKAogICAgICAgIHN1bW1hcnkobW9kX211bHRpX29zKSRjb2VmZmljaWVudHMgJT4lIGRhdGEuZnJhbWUoKSAlPiUgZHBseXI6OnJlbmFtZShIUiA9ICdleHAuY29lZi4nLCBIUnNlID0gJ3NlLmNvZWYuJywgc3RhdGlzdGljID0gJ3onLCBwdmFsdWUgPSAnUHIuLi56Li4nKSAlPiUgCiAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbigndmFyaWFibGUnKSAlPiUgbXV0YXRlKExpbmVhZ2UgPSBsaW5lYWdlLCBtdWx0aXZhcmlhYmxlID0gVFJVRSwgc3Vydml2YWwgPSAnT1MnKSAlPiUgCiAgICAgICAgICAgIG11dGF0ZShIUl91cHIgPSBIUiArIDEuOTYqSFJzZSwgSFJfbHdyID0gSFIgLSAxLjk2KkhSc2UsIAogICAgICAgICAgICAgICAgICAgbl9wYXRpZW50cyA9IG1vZF9tdWx0aV9vcyRuKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcih2YXJpYWJsZSAlPiUgc3RyX2RldGVjdCgnbGluZWFnZScpKSAlPiUgc2VsZWN0KExpbmVhZ2UsIHN1cnZpdmFsLCBtdWx0aXZhcmlhYmxlLCBIUiwgSFJzZSwgSFJfbHdyLCBIUl91cHIsIG5fcGF0aWVudHMsIHN0YXRpc3RpYywgcHZhbHVlKSAlPiUgCiAgICAgICAgICAgICMgbmVzdGVkIExSVDogaG93IG11Y2ggcHJvZ25vc3RpYyBpbmZvIGRvZXMgZGV2IHN0YXRlIGFkZCBiZXlvbmQgYmFzZWxpbmUgbW9kZWw/CiAgICAgICAgICAgIG11dGF0ZShwdmFsdWVfbmVzdGVkTFJUID0gYW5vdmEobW9kX211bHRpX29zLCBtb2RfbXVsdGlfb3NfMClbMiwnUCg+fENoaXwpJ10pCiAgICAgICAgCiAgICApICU+JSBiaW5kX3Jvd3MoCiAgICAgIAogICAgICAgIHN1bW1hcnkobW9kX211bHRpX2VmcykkY29lZmZpY2llbnRzICU+JSBkYXRhLmZyYW1lKCkgJT4lIGRwbHlyOjpyZW5hbWUoSFIgPSAnZXhwLmNvZWYuJywgSFJzZSA9ICdzZS5jb2VmLicsIHN0YXRpc3RpYyA9ICd6JywgcHZhbHVlID0gJ1ByLi4uei4uJykgJT4lIAogICAgICAgICAgICByb3duYW1lc190b19jb2x1bW4oJ3ZhcmlhYmxlJykgJT4lIG11dGF0ZShMaW5lYWdlID0gbGluZWFnZSwgbXVsdGl2YXJpYWJsZSA9IFRSVUUsIHN1cnZpdmFsID0gJ0VGUycpICU+JSAKICAgICAgICAgICAgbXV0YXRlKEhSX3VwciA9IEhSICsgMS45NipIUnNlLCBIUl9sd3IgPSBIUiAtIDEuOTYqSFJzZSwgCiAgICAgICAgICAgICAgICAgICBuX3BhdGllbnRzID0gbW9kX211bHRpX2VmcyRuKSAlPiUKICAgICAgICAgICAgZmlsdGVyKHZhcmlhYmxlICU+JSBzdHJfZGV0ZWN0KCdsaW5lYWdlJykpICU+JSBzZWxlY3QoTGluZWFnZSwgc3Vydml2YWwsIG11bHRpdmFyaWFibGUsIEhSLCBIUnNlLCBIUl9sd3IsIEhSX3Vwciwgbl9wYXRpZW50cywgc3RhdGlzdGljLCBwdmFsdWUpICU+JSAKICAgICAgICAgICAgIyBuZXN0ZWQgTFJUOiBob3cgbXVjaCBwcm9nbm9zdGljIGluZm8gZG9lcyBkZXYgc3RhdGUgYWRkIGJleW9uZCBiYXNlbGluZSBtb2RlbD8KICAgICAgICAgICAgbXV0YXRlKHB2YWx1ZV9uZXN0ZWRMUlQgPSBhbm92YShtb2RfbXVsdGlfZWZzLCBtb2RfbXVsdGlfZWZzXzApWzIsJ1AoPnxDaGl8KSddKQogICAgICAgIAogICAgKQp9CgpsaW5lYWdlX3N1cnZpdmFsX3BlZF9NUkRuZWcgCmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gM30Kc3Vydl9wZWRfbXVsdGlfTVJEbmVnIDwtIGxpbmVhZ2Vfc3Vydml2YWxfcGVkX01SRG5lZyAlPiUgbXV0YXRlKGxvZ3B2YWwgPSBpZmVsc2UoSFIgPiAxLCAtbG9nMTAocHZhbHVlKSwgbG9nMTAocHZhbHVlKSkpICU+JSAKICAgIG11dGF0ZShBc3NvY2lhdGlvbiA9IGlmZWxzZShIUl9sd3IgPiAxICYgcHZhbHVlX25lc3RlZExSVCA8IDAuMDUsICdBZHZlcnNlJywgaWZlbHNlKEhSX3VwciA8IDEgJiBwdmFsdWVfbmVzdGVkTFJUIDwgMC4wNSwgJ0Zhdm9yYWJsZScsICdOLlMuJykpKSAlPiUKICAgIGZpbHRlcihtdWx0aXZhcmlhYmxlID09IFRSVUUpICU+JSAKICAgIG11dGF0ZShzdXJ2bmFtZSA9IGlmZWxzZShzdXJ2aXZhbCA9PSAnT1MnLCAnT3ZlcmFsbCBTdXJ2aXZhbCcsIGlmZWxzZShzdXJ2aXZhbCA9PSAnRUZTJywgJ0V2ZW50LUZyZWUgU3Vydml2YWwnLCAnTkEnKSkgJT4lIAogICAgICAgICAgIGZhY3RvcihsZXZlbHMgPSBjKCdPdmVyYWxsIFN1cnZpdmFsJywgJ0V2ZW50LUZyZWUgU3Vydml2YWwnKSkpICU+JQogICAgbXV0YXRlKExpbmVhZ2UgPSBpZmVsc2UoTGluZWFnZSAlaW4lIGMoJ015ZWxvaWRfUHJvZycsICdFYXJseV9MeW1waG9pZCcsICdNYXR1cmVfQicsICdNdWx0aXBvdGVuY3lfU2NvcmUnKSwgTGluZWFnZSAlPiUgc3RyX3JlcGxhY2UoJ1Byb2cnLCAnUHJvZ2VuaXRvcicpICU+JSBzdHJfcmVwbGFjZSgnXycsICcgJyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgTGluZWFnZSAlPiUgc3RyX3JlcGxhY2UoJ19NUFAnLCAnL01QUCcpICU+JSBzdHJfcmVwbGFjZSgnX05LJywgJy9OSycpICU+JSBzdHJfcmVwbGFjZSgnXycsICctJykpICU+JSAKICAgICAgICAgICAgIGZhY3RvcihsZXZlbHMgPSBjKCdNdWx0aXBvdGVuY3kgU2NvcmUnLCdIU0MvTVBQJywgJ015ZWxvaWQgUHJvZ2VuaXRvcicsICdQcmUtcERDJywgJ0Vhcmx5IEx5bXBob2lkJywgJ1Byby1CJywgJ1ByZS1CJykpKSAlPiUgZmlsdGVyKExpbmVhZ2UgIT0gJ05BJykgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBMaW5lYWdlLCB5ID0gSFIsIHltYXggPSBIUl91cHIsIHltaW4gPSBIUl9sd3IsIGNvbG9yID0gQXNzb2NpYXRpb24pKSArIAogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbHR5ID0gMiwgc2l6ZSA9IDAuNSwgYWxwaGEgPSAxLCBjb2xvciA9ICdkYXJrZ3JleScpICsgCiAgICBnZW9tX3BvaW50cmFuZ2Uoc2l6ZT0wLjgsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPWMoMCkpKSArCiAgICBmYWNldF93cmFwKC5+c3Vydm5hbWUsIG5jb2w9MSwgc2NhbGVzID0gJ2ZyZWVfeCcpICsgCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ2luZGlhbnJlZDQnLCAnZGFya2dyZWVuJywgJyM2NjY2NjYnKSkgKyAgIAogICAgdGhlbWVfcHVicihsZWdlbmQgPSAncmlnaHQnKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3Mobj04KSwgbGltaXRzID0gYygwLjIsMi4xKSkgKyB0aGVtZShheGlzLnRpdGxlLng9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICAgIHlsYWIoJ011bHRpdmFyaWFibGUgSGF6YXJkIFJhdGlvICAocGVyIDFTRCBpbmNyZWFzZSBpbiBhYnVuZGFuY2UpJykgKyAKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMC41LCBhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTEpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEwLjUpLAogICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLjUpKSArIAogICAgZ2d0aXRsZSgnTXVsdGl2YXJpYWJsZSBTdXJ2aXZhbFxuKE1SRCBuZWdhdGl2ZSBwYXRpZW50czsgbiA9IDY0OSknKQogICAgCiNnZ3NhdmUocGxvdCA9IHN1cnZfcGVkX3VuaSwgZmlsZW5hbWUgPSAnQkFMTF9zdXJ2aXZhbF9maWd1cmVzX2ZpbmFsL1N1cnZpdmFsX3BlZGlhdHJpY19Vbml2YXJpYWJsZV9CRGV2T25seV9IYXphcmRSYXRpb3MucGRmJywgZGV2aWNlID0gJ3BkZicsIGhlaWdodCA9IDcuNSwgd2lkdGggPSA0LjUpCiNnZ3NhdmUocGxvdCA9IHN1cnZfcGVkX211bHRpX01SRG5lZywgZmlsZW5hbWUgPSAnQkFMTF9zdXJ2aXZhbF9maWd1cmVzX2ZpbmFsL1N1cnZpdmFsX3BlZGlhdHJpY19NdWx0aXZhcmlhYmxlX0JEZXZPbmx5X0hhemFyZFJhdGlvc19NUkRuZWdhdGl2ZS5wZGYnLCBkZXZpY2UgPSAncGRmJywgaGVpZ2h0ID0gNy41LCB3aWR0aCA9IDQuNSkKCnN1cnZfcGVkX211bHRpX01SRG5lZwpgYGAKCiMjIyBBZHVsdCBTdXJ2aXZhbCAKCjMyNCBhZHVsdCBCLUFMTCBwYXRpZW50cyB3aXRoIHN1cnZpdmFsIGRhdGEgCgpgYGB7cn0KIyBVc2UgYWR1bHQgY29ob3J0IEVDT0cKYnVsazIwNDZfYWR1bHQgPC0gYnVsazIwNDYgJT4lIAogICAgZmlsdGVyKCFJbnN0aXR1dGUgJWluJSBjKCdDT0cnLCAnU3QgSnVkZScpKSAlPiUgCiAgICBmaWx0ZXIoIWlzLm5hKG9zY2Vuc29yKSB8ICFpcy5uYShlZnNjZW5zb3IpKSAlPiUgCiAgICBtdXRhdGUoUmlza19Hcm91cCA9IGZhY3RvcihSaXNrX0dyb3VwLCBsZXZlbHMgPSBjKCdBWUEnLCAnQWR1bHQnKSksICAjIGNsaW5pY2FsIHJpc2sgZ3JvdXAKICAgICAgICAgICBHZW5vbWljUmlza19hZHVsdCA9IGZhY3RvcihHZW5vbWljUmlza19hZHVsdCwgbGV2ZWxzID0gYygnVW5jbGFzc2lmaWVkJywgJ0Zhdm9yYWJsZScsICdJbnRlcm1lZGlhdGUnLCAnVW5mYXZvcmFibGUnKSkpICAgICAjIGdlbm9taWMgcmlzayBncm91cAoKYnVsazIwNDZfYWR1bHQKYGBgCgpMb29wIHRocm91Z2ggZGV2ZWxvcG1lbnRhbCBzdGF0ZXMgYW5kIHBlcmZvcm0gdW5pdmFyaWFibGUgKyBtdWx0aXZhcmlhYmxlIGNveCByZWdyZXNzaW9uCgpgYGB7cn0KIyBkYXRhZnJhbWUgdG8gc3RvcmUgcmVzdWx0cwpsaW5lYWdlX3N1cnZpdmFsX2FkdWx0IDwtIGRhdGEuZnJhbWUoKQpkZXZzdGF0ZXMgPC0gYygnTXVsdGlwb3RlbmN5X1Njb3JlJywgJ0hTQ19NUFAnLCAnTXllbG9pZF9Qcm9nJywgJ1ByZV9wREMnLCAnRWFybHlfTHltcGhvaWQnLCAnUHJvX0InLCAnUHJlX0InLCAnTWF0dXJlX0InLCAnVF9OSycsICdNb25vY3l0ZScsICdFcnl0aHJvaWQnKQoKZm9yKGxpbmVhZ2UgaW4gZGV2c3RhdGVzKXsKCiAgICAjIFVuaXZhcmlhYmxlIG1vZGVsIHN0cmF0aWZpeWluZyBvbiBpbnN0aXR1dGUgYW5kIHByaW1hcnkgc3VidHlwZQogICAgbW9kX29zIDwtIGNveHBoKFN1cnYob3N0aW1lLCBvc2NlbnNvcikgfiBnZXQobGluZWFnZSksIGJ1bGsyMDQ2X2FkdWx0KQogICAgbW9kX2VmcyA8LSBjb3hwaChTdXJ2KGVmc3RpbWUsIGVmc2NlbnNvcikgfiBnZXQobGluZWFnZSksIGJ1bGsyMDQ2X2FkdWx0KQoKICAgIGxpbmVhZ2Vfc3Vydml2YWxfYWR1bHQgPC0gbGluZWFnZV9zdXJ2aXZhbF9hZHVsdCAlPiUgYmluZF9yb3dzKAogICAgICAgIHN1bW1hcnkobW9kX29zKSRjb2VmZmljaWVudHMgJT4lIGRhdGEuZnJhbWUoKSAlPiUgZHBseXI6OnJlbmFtZShIUiA9ICdleHAuY29lZi4nLCBIUnNlID0gJ3NlLmNvZWYuJywgc3RhdGlzdGljID0gJ3onLCBwdmFsdWUgPSAnUHIuLi56Li4nKSAlPiUgCiAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbigndmFyaWFibGUnKSAlPiUgbXV0YXRlKExpbmVhZ2UgPSBsaW5lYWdlLCBtdWx0aXZhcmlhYmxlID0gRkFMU0UsIHN1cnZpdmFsID0gJ09TJykgJT4lIAogICAgICAgICAgICBtdXRhdGUoSFJfdXByID0gSFIgKyAxLjk2KkhSc2UsIEhSX2x3ciA9IEhSIC0gMS45NipIUnNlLCAKICAgICAgICAgICAgICAgICAgIG5fcGF0aWVudHMgPSBtb2Rfb3MkbikgJT4lIAogICAgICAgICAgICBmaWx0ZXIodmFyaWFibGUgJT4lIHN0cl9kZXRlY3QoJ2xpbmVhZ2UnKSkgJT4lIHNlbGVjdChMaW5lYWdlLCBzdXJ2aXZhbCwgbXVsdGl2YXJpYWJsZSwgSFIsIEhSc2UsIEhSX2x3ciwgSFJfdXByLCBuX3BhdGllbnRzLCBzdGF0aXN0aWMsIHB2YWx1ZSkKICAgICkgJT4lIGJpbmRfcm93cygKICAgICAgICBzdW1tYXJ5KG1vZF9lZnMpJGNvZWZmaWNpZW50cyAlPiUgZGF0YS5mcmFtZSgpICU+JSBkcGx5cjo6cmVuYW1lKEhSID0gJ2V4cC5jb2VmLicsIEhSc2UgPSAnc2UuY29lZi4nLCBzdGF0aXN0aWMgPSAneicsIHB2YWx1ZSA9ICdQci4uLnouLicpICU+JSAKICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCd2YXJpYWJsZScpICU+JSBtdXRhdGUoTGluZWFnZSA9IGxpbmVhZ2UsIG11bHRpdmFyaWFibGUgPSBGQUxTRSwgc3Vydml2YWwgPSAnRUZTJykgJT4lIAogICAgICAgICAgICBtdXRhdGUoSFJfdXByID0gSFIgKyAxLjk2KkhSc2UsIEhSX2x3ciA9IEhSIC0gMS45NipIUnNlLCAKICAgICAgICAgICAgICAgICAgIG5fcGF0aWVudHMgPSBtb2RfZWZzJG4pICU+JSAKICAgICAgICAgICAgZmlsdGVyKHZhcmlhYmxlICU+JSBzdHJfZGV0ZWN0KCdsaW5lYWdlJykpICU+JSBzZWxlY3QoTGluZWFnZSwgc3Vydml2YWwsIG11bHRpdmFyaWFibGUsIEhSLCBIUnNlLCBIUl9sd3IsIEhSX3Vwciwgbl9wYXRpZW50cywgc3RhdGlzdGljLCBwdmFsdWUpCiAgICApCiAgICAKICAgICMgTXVsdGl2YXJpYWJsZSBtb2RlbCBzdHJhdGlmaXlpbmcgb24gaW5zdGl0dXRlIGFuZCBwcmltYXJ5IHN1YnR5cGUKICAgIG1vZF9tdWx0aV9vcyA8LSBjb3hwaChTdXJ2KG9zdGltZSwgb3NjZW5zb3IpIH4gZ2V0KGxpbmVhZ2UpICsgUmlza19Hcm91cCArIEdlbm9taWNSaXNrX2FkdWx0ICsgU2V4ICsgQWdlICsgV0JDLCBidWxrMjA0Nl9hZHVsdCkKICAgIG1vZF9tdWx0aV9lZnMgPC0gY294cGgoU3VydihlZnN0aW1lLCBlZnNjZW5zb3IpIH4gZ2V0KGxpbmVhZ2UpICsgUmlza19Hcm91cCArIEdlbm9taWNSaXNrX2FkdWx0ICsgU2V4ICsgQWdlICsgV0JDLCBidWxrMjA0Nl9hZHVsdCkKICAgIAogICAgIyBDcmVhdGUgYmFzZWxpbmUgbW9kZWxzIHdpdGhvdXQgZGV2IHN0YXRlIGFidW5kYW5jZSB0byBjb21wYXJlIGFnYWluc3QgYnkgbmVzdGVkIExSVAogICAgbW9kX211bHRpX29zXzAgPC0gY294cGgoU3Vydihvc3RpbWUsIG9zY2Vuc29yKSB+IFJpc2tfR3JvdXAgKyBHZW5vbWljUmlza19hZHVsdCArIFNleCArIEFnZSArIFdCQywgYnVsazIwNDZfYWR1bHQpCiAgICBtb2RfbXVsdGlfZWZzXzAgPC0gY294cGgoU3VydihlZnN0aW1lLCBlZnNjZW5zb3IpIH4gUmlza19Hcm91cCArIEdlbm9taWNSaXNrX2FkdWx0ICsgU2V4ICsgQWdlICsgV0JDLCBidWxrMjA0Nl9hZHVsdCkKCiAgICBsaW5lYWdlX3N1cnZpdmFsX2FkdWx0IDwtIGxpbmVhZ2Vfc3Vydml2YWxfYWR1bHQgJT4lIGJpbmRfcm93cygKICAgICAgICBzdW1tYXJ5KG1vZF9tdWx0aV9vcykkY29lZmZpY2llbnRzICU+JSBkYXRhLmZyYW1lKCkgJT4lIGRwbHlyOjpyZW5hbWUoSFIgPSAnZXhwLmNvZWYuJywgSFJzZSA9ICdzZS5jb2VmLicsIHN0YXRpc3RpYyA9ICd6JywgcHZhbHVlID0gJ1ByLi4uei4uJykgJT4lIAogICAgICAgICAgICByb3duYW1lc190b19jb2x1bW4oJ3ZhcmlhYmxlJykgJT4lIG11dGF0ZShMaW5lYWdlID0gbGluZWFnZSwgbXVsdGl2YXJpYWJsZSA9IFRSVUUsIHN1cnZpdmFsID0gJ09TJykgJT4lIAogICAgICAgICAgICBtdXRhdGUoSFJfdXByID0gSFIgKyAxLjk2KkhSc2UsIEhSX2x3ciA9IEhSIC0gMS45NipIUnNlLCAKICAgICAgICAgICAgICAgICAgIG5fcGF0aWVudHMgPSBtb2RfbXVsdGlfb3MkbikgJT4lIAogICAgICAgICAgICBmaWx0ZXIodmFyaWFibGUgJT4lIHN0cl9kZXRlY3QoJ2xpbmVhZ2UnKSkgJT4lIHNlbGVjdChMaW5lYWdlLCBzdXJ2aXZhbCwgbXVsdGl2YXJpYWJsZSwgSFIsIEhSc2UsIEhSX2x3ciwgSFJfdXByLCBuX3BhdGllbnRzLCBzdGF0aXN0aWMsIHB2YWx1ZSkgJT4lIAogICAgICAgICAgICAjIG5lc3RlZCBMUlQ6IGhvdyBtdWNoIHByb2dub3N0aWMgaW5mbyBkb2VzIGRldiBzdGF0ZSBhZGQgYmV5b25kIGJhc2VsaW5lIG1vZGVsPwogICAgICAgICAgICBtdXRhdGUocHZhbHVlX25lc3RlZExSVCA9IGFub3ZhKG1vZF9tdWx0aV9vcywgbW9kX211bHRpX29zXzApWzIsJ1AoPnxDaGl8KSddKQogICAgICAgIAogICAgKSAlPiUgYmluZF9yb3dzKAogICAgICAKICAgICAgICBzdW1tYXJ5KG1vZF9tdWx0aV9lZnMpJGNvZWZmaWNpZW50cyAlPiUgZGF0YS5mcmFtZSgpICU+JSBkcGx5cjo6cmVuYW1lKEhSID0gJ2V4cC5jb2VmLicsIEhSc2UgPSAnc2UuY29lZi4nLCBzdGF0aXN0aWMgPSAneicsIHB2YWx1ZSA9ICdQci4uLnouLicpICU+JSAKICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCd2YXJpYWJsZScpICU+JSBtdXRhdGUoTGluZWFnZSA9IGxpbmVhZ2UsIG11bHRpdmFyaWFibGUgPSBUUlVFLCBzdXJ2aXZhbCA9ICdFRlMnKSAlPiUgCiAgICAgICAgICAgIG11dGF0ZShIUl91cHIgPSBIUiArIDEuOTYqSFJzZSwgSFJfbHdyID0gSFIgLSAxLjk2KkhSc2UsIAogICAgICAgICAgICAgICAgICAgbl9wYXRpZW50cyA9IG1vZF9tdWx0aV9lZnMkbikgJT4lIAogICAgICAgICAgICBmaWx0ZXIodmFyaWFibGUgJT4lIHN0cl9kZXRlY3QoJ2xpbmVhZ2UnKSkgJT4lIHNlbGVjdChMaW5lYWdlLCBzdXJ2aXZhbCwgbXVsdGl2YXJpYWJsZSwgSFIsIEhSc2UsIEhSX2x3ciwgSFJfdXByLCBuX3BhdGllbnRzLCBzdGF0aXN0aWMsIHB2YWx1ZSkgJT4lIAogICAgICAgICAgICAjIG5lc3RlZCBMUlQ6IGhvdyBtdWNoIHByb2dub3N0aWMgaW5mbyBkb2VzIGRldiBzdGF0ZSBhZGQgYmV5b25kIGJhc2VsaW5lIG1vZGVsPwogICAgICAgICAgICBtdXRhdGUocHZhbHVlX25lc3RlZExSVCA9IGFub3ZhKG1vZF9tdWx0aV9lZnMsIG1vZF9tdWx0aV9lZnNfMClbMiwnUCg+fENoaXwpJ10pCiAgICAgICAgCiAgICApCn0KCmxpbmVhZ2Vfc3Vydml2YWxfYWR1bHQKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA2fQpzdXJ2X2FkdWx0X3VuaSA8LSBsaW5lYWdlX3N1cnZpdmFsX2FkdWx0ICU+JSBtdXRhdGUobG9ncHZhbCA9IGlmZWxzZShIUiA+IDEsIC1sb2cxMChwdmFsdWUpLCBsb2cxMChwdmFsdWUpKSkgJT4lIAogICAgbXV0YXRlKEFzc29jaWF0aW9uID0gaWZlbHNlKEhSX2x3ciA+IDEgJiBwdmFsdWUgPCAwLjA1LCAnQWR2ZXJzZScsIGlmZWxzZShIUl91cHIgPCAxICYgcHZhbHVlIDwgMC4wNSwgJ0Zhdm9yYWJsZScsICdOLlMuJykpKSAlPiUKICAgIGZpbHRlcihtdWx0aXZhcmlhYmxlID09IEZBTFNFKSAlPiUgCiAgICBtdXRhdGUoc3Vydm5hbWUgPSBpZmVsc2Uoc3Vydml2YWwgPT0gJ09TJywgJ092ZXJhbGwgU3Vydml2YWwnLCBpZmVsc2Uoc3Vydml2YWwgPT0gJ0VGUycsICdFdmVudC1GcmVlIFN1cnZpdmFsJywgJ05BJykpICU+JSAKICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnT3ZlcmFsbCBTdXJ2aXZhbCcsICdFdmVudC1GcmVlIFN1cnZpdmFsJykpKSAlPiUKICAgIG11dGF0ZShMaW5lYWdlID0gaWZlbHNlKExpbmVhZ2UgJWluJSBjKCdNeWVsb2lkX1Byb2cnLCAnRWFybHlfTHltcGhvaWQnLCAnTWF0dXJlX0InLCAnTXVsdGlwb3RlbmN5X1Njb3JlJyksIExpbmVhZ2UgJT4lIHN0cl9yZXBsYWNlKCdQcm9nJywgJ1Byb2dlbml0b3InKSAlPiUgc3RyX3JlcGxhY2UoJ18nLCAnICcpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIExpbmVhZ2UgJT4lIHN0cl9yZXBsYWNlKCdfTVBQJywgJy9NUFAnKSAlPiUgc3RyX3JlcGxhY2UoJ19OSycsICcvTksnKSAlPiUgc3RyX3JlcGxhY2UoJ18nLCAnLScpKSAlPiUgCiAgICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnTXVsdGlwb3RlbmN5IFNjb3JlJywgJ1BDMScsICdQQzEtbmV3JywgJ0hTQy9NUFAnLCAnTXllbG9pZCBQcm9nZW5pdG9yJywgJ1ByZS1wREMnLCAnRWFybHkgTHltcGhvaWQnLCAnUHJvLUInLCAnUHJlLUInKSkpICU+JSBmaWx0ZXIoTGluZWFnZSAhPSAnTkEnKSAlPiUKICAgIGdncGxvdChhZXMoeCA9IExpbmVhZ2UsIHkgPSBIUiwgeW1heCA9IEhSX3VwciwgeW1pbiA9IEhSX2x3ciwgY29sb3IgPSBBc3NvY2lhdGlvbikpICsgCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsdHkgPSAyLCBzaXplID0gMC41LCBhbHBoYSA9IDEsIGNvbG9yID0gJ2RhcmtncmV5JykgKyAKICAgIGdlb21fcG9pbnRyYW5nZShzaXplPTAuOCwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9YygwKSkpICsKICAgIGZhY2V0X3dyYXAoLn5zdXJ2bmFtZSwgbmNvbD0xLCBzY2FsZXMgPSAnZnJlZV94JykgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnaW5kaWFucmVkNCcsICdkYXJrZ3JlZW4nLCAnIzY2NjY2NicpKSArICAgCiAgICB0aGVtZV9wdWJyKGxlZ2VuZCA9ICdyaWdodCcpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNjYWxlczo6cHJldHR5X2JyZWFrcyhuPTgpLCBsaW1pdHMgPSBjKDAuNSwxLjUpKSArIHRoZW1lKGF4aXMudGl0bGUueD0gZWxlbWVudF9ibGFuaygpKSArIAogICAgeWxhYignVW5pdmFyaWFibGUgSGF6YXJkIFJhdGlvICAocGVyIDFTRCBpbmNyZWFzZSBpbiBhYnVuZGFuY2UpJykgKyAKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMC41LCBhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTEpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEwLjUpLAogICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLjUpKSArIAogICAgZ2d0aXRsZSgnVW5pdmFyaWFibGUgLSBBZHVsdCBCLUFMTCAobiA9IDMyNCknKQogICAgCnN1cnZfYWR1bHRfbXVsdGkgPC0gbGluZWFnZV9zdXJ2aXZhbF9hZHVsdCAlPiUgbXV0YXRlKGxvZ3B2YWwgPSBpZmVsc2UoSFIgPiAxLCAtbG9nMTAocHZhbHVlKSwgbG9nMTAocHZhbHVlKSkpICU+JSAKICAgIG11dGF0ZShBc3NvY2lhdGlvbiA9IGlmZWxzZShIUl9sd3IgPiAxICYgcHZhbHVlX25lc3RlZExSVCA8IDAuMDUsICdBZHZlcnNlJywgaWZlbHNlKEhSX3VwciA8IDEgJiBwdmFsdWVfbmVzdGVkTFJUIDwgMC4wNSwgJ0Zhdm9yYWJsZScsICdOLlMuJykpKSAlPiUKICAgIGZpbHRlcihtdWx0aXZhcmlhYmxlID09IFRSVUUpICU+JSAKICAgIG11dGF0ZShzdXJ2bmFtZSA9IGlmZWxzZShzdXJ2aXZhbCA9PSAnT1MnLCAnT3ZlcmFsbCBTdXJ2aXZhbCcsIGlmZWxzZShzdXJ2aXZhbCA9PSAnRUZTJywgJ0V2ZW50LUZyZWUgU3Vydml2YWwnLCAnTkEnKSkgJT4lIAogICAgICAgICAgIGZhY3RvcihsZXZlbHMgPSBjKCdPdmVyYWxsIFN1cnZpdmFsJywgJ0V2ZW50LUZyZWUgU3Vydml2YWwnKSkpICU+JQogICAgbXV0YXRlKExpbmVhZ2UgPSBpZmVsc2UoTGluZWFnZSAlaW4lIGMoJ015ZWxvaWRfUHJvZycsICdFYXJseV9MeW1waG9pZCcsICdNYXR1cmVfQicsICdNdWx0aXBvdGVuY3lfU2NvcmUnKSwgTGluZWFnZSAlPiUgc3RyX3JlcGxhY2UoJ1Byb2cnLCAnUHJvZ2VuaXRvcicpICU+JSBzdHJfcmVwbGFjZSgnXycsICcgJyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgTGluZWFnZSAlPiUgc3RyX3JlcGxhY2UoJ19NUFAnLCAnL01QUCcpICU+JSBzdHJfcmVwbGFjZSgnX05LJywgJy9OSycpICU+JSBzdHJfcmVwbGFjZSgnXycsICctJykpICU+JSAKICAgICAgICAgICAgIGZhY3RvcihsZXZlbHMgPSBjKCdNdWx0aXBvdGVuY3kgU2NvcmUnLCAnUEMxJywgJ1BDMS1uZXcnLCAnSFNDL01QUCcsICdNeWVsb2lkIFByb2dlbml0b3InLCAnUHJlLXBEQycsICdFYXJseSBMeW1waG9pZCcsICdQcm8tQicsICdQcmUtQicpKSkgJT4lIGZpbHRlcihMaW5lYWdlICE9ICdOQScpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gTGluZWFnZSwgeSA9IEhSLCB5bWF4ID0gSFJfdXByLCB5bWluID0gSFJfbHdyLCBjb2xvciA9IEFzc29jaWF0aW9uKSkgKyAKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGx0eSA9IDIsIHNpemUgPSAwLjUsIGFscGhhID0gMSwgY29sb3IgPSAnZGFya2dyZXknKSArIAogICAgZ2VvbV9wb2ludHJhbmdlKHNpemU9MC44LCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD1jKDApKSkgKwogICAgZmFjZXRfd3JhcCgufnN1cnZuYW1lLCBuY29sPTEsIHNjYWxlcyA9ICdmcmVlX3gnKSArIAogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCcjNjY2NjY2JykpICsgICAKICAgIHRoZW1lX3B1YnIobGVnZW5kID0gJ3JpZ2h0JykgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2NhbGVzOjpwcmV0dHlfYnJlYWtzKG49OCksIGxpbWl0cyA9IGMoMC41LDEuNSkpICsgdGhlbWUoYXhpcy50aXRsZS54PSBlbGVtZW50X2JsYW5rKCkpICsgCiAgICB5bGFiKCdNdWx0aXZhcmlhYmxlIEhhemFyZCBSYXRpbyAgKHBlciAxU0QgaW5jcmVhc2UgaW4gYWJ1bmRhbmNlKScpICsgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAuNSwgYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTExKSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMC41KSwKICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMC41KSkgKyAKICAgIGdndGl0bGUoJ011bHRpdmFyaWFibGUgLSBBZHVsdCBCLUFMTCAobiA9IDMxMiknKQogICAgCiNnZ3NhdmUocGxvdCA9IHN1cnZfYWR1bHRfdW5pLCBmaWxlbmFtZSA9ICdCQUxMX3N1cnZpdmFsX2ZpZ3VyZXNfZmluYWwvU3Vydml2YWxfYWR1bHRfVW5pdmFyaWFibGVfQkRldk9ubHlfSGF6YXJkUmF0aW9zLnBkZicsIGRldmljZSA9ICdwZGYnLCBoZWlnaHQgPSA3LjUsIHdpZHRoID0gNC41KQojZ2dzYXZlKHBsb3QgPSBzdXJ2X2FkdWx0X211bHRpLCBmaWxlbmFtZSA9ICdCQUxMX3N1cnZpdmFsX2ZpZ3VyZXNfZmluYWwvU3Vydml2YWxfYWR1bHRfTXVsdGl2YXJpYWJsZV9CRGV2T25seV9IYXphcmRSYXRpb3MucGRmJywgZGV2aWNlID0gJ3BkZicsIGhlaWdodCA9IDcuNSwgd2lkdGggPSA0LjUpCgpzdXJ2X2FkdWx0X3VuaSB8IHN1cnZfYWR1bHRfbXVsdGkKYGBgCgoKCgojIyMgUmUtcnVuIHdpdGhpbiBCQ1I6OkFCTDEgcGF0aWVudHMKCmBgYHtyfQojIEtlZXAgQkNSOjpBQkwxIHBhdGllbnRzCmJ1bGsyMDQ2X3BoIDwtIGJ1bGsyMDQ2ICU+JSBmaWx0ZXIobmV3X1N1YnR5cGUgPT0gJ0JDUjo6QUJMMScpICAKCmJ1bGsyMDQ2X3BoCmJ1bGsyMDQ2X3BoICU+JSBmaWx0ZXIob3NjZW5zb3IgJWluJSBjKCcwJywnMScpKSAlPiUgcHVsbChJbnN0aXR1dGUpICU+JSB0YWJsZSgpCmBgYAoKCkxvb3AgdGhyb3VnaCBkZXZlbG9wbWVudGFsIHN0YXRlcyBhbmQgcGVyZm9ybSB1bml2YXJpYWJsZSArIG11bHRpdmFyaWFibGUgY294IHJlZ3Jlc3Npb24KCmBgYHtyfQojIGRhdGFmcmFtZSB0byBzdG9yZSByZXN1bHRzCmxpbmVhZ2Vfc3Vydml2YWxfcGggPC0gZGF0YS5mcmFtZSgpCmRldnN0YXRlcyA8LSBjKCdNdWx0aXBvdGVuY3lfU2NvcmUnLCAnSFNDX01QUCcsICdNeWVsb2lkX1Byb2cnLCAnUHJlX3BEQycsICdFYXJseV9MeW1waG9pZCcsICdQcm9fQicsICdQcmVfQicpCgpmb3IobGluZWFnZSBpbiBkZXZzdGF0ZXMpewoKICAgICMgVW5pdmFyaWFibGUgbW9kZWwgc3RyYXRpZml5aW5nIG9uIGluc3RpdHV0ZSBhbmQgcHJpbWFyeSBzdWJ0eXBlCiAgICBtb2Rfb3MgPC0gY294cGgoU3Vydihvc3RpbWUsIG9zY2Vuc29yKSB+IGdldChsaW5lYWdlKSwgYnVsazIwNDZfcGgpCiAgICBtb2RfZWZzIDwtIGNveHBoKFN1cnYoZWZzdGltZSwgZWZzY2Vuc29yKSB+IGdldChsaW5lYWdlKSwgYnVsazIwNDZfcGgpCgogICAgbGluZWFnZV9zdXJ2aXZhbF9waCA8LSBsaW5lYWdlX3N1cnZpdmFsX3BoICU+JSBiaW5kX3Jvd3MoCiAgICAgICAgc3VtbWFyeShtb2Rfb3MpJGNvZWZmaWNpZW50cyAlPiUgZGF0YS5mcmFtZSgpICU+JSBkcGx5cjo6cmVuYW1lKEhSID0gJ2V4cC5jb2VmLicsIEhSc2UgPSAnc2UuY29lZi4nLCBzdGF0aXN0aWMgPSAneicsIHB2YWx1ZSA9ICdQci4uLnouLicpICU+JSAKICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCd2YXJpYWJsZScpICU+JSBtdXRhdGUoTGluZWFnZSA9IGxpbmVhZ2UsIG11bHRpdmFyaWFibGUgPSBGQUxTRSwgc3Vydml2YWwgPSAnT1MnKSAlPiUgCiAgICAgICAgICAgIG11dGF0ZShIUl91cHIgPSBIUiArIDEuOTYqSFJzZSwgSFJfbHdyID0gSFIgLSAxLjk2KkhSc2UsIAogICAgICAgICAgICAgICAgICAgbl9wYXRpZW50cyA9IG1vZF9vcyRuKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcih2YXJpYWJsZSAlPiUgc3RyX2RldGVjdCgnbGluZWFnZScpKSAlPiUgc2VsZWN0KExpbmVhZ2UsIHN1cnZpdmFsLCBtdWx0aXZhcmlhYmxlLCBIUiwgSFJzZSwgSFJfbHdyLCBIUl91cHIsIG5fcGF0aWVudHMsIHN0YXRpc3RpYywgcHZhbHVlKQogICAgKSAlPiUgYmluZF9yb3dzKAogICAgICAgIHN1bW1hcnkobW9kX2VmcykkY29lZmZpY2llbnRzICU+JSBkYXRhLmZyYW1lKCkgJT4lIGRwbHlyOjpyZW5hbWUoSFIgPSAnZXhwLmNvZWYuJywgSFJzZSA9ICdzZS5jb2VmLicsIHN0YXRpc3RpYyA9ICd6JywgcHZhbHVlID0gJ1ByLi4uei4uJykgJT4lIAogICAgICAgICAgICByb3duYW1lc190b19jb2x1bW4oJ3ZhcmlhYmxlJykgJT4lIG11dGF0ZShMaW5lYWdlID0gbGluZWFnZSwgbXVsdGl2YXJpYWJsZSA9IEZBTFNFLCBzdXJ2aXZhbCA9ICdFRlMnKSAlPiUgCiAgICAgICAgICAgIG11dGF0ZShIUl91cHIgPSBIUiArIDEuOTYqSFJzZSwgSFJfbHdyID0gSFIgLSAxLjk2KkhSc2UsIAogICAgICAgICAgICAgICAgICAgbl9wYXRpZW50cyA9IG1vZF9lZnMkbikgJT4lIAogICAgICAgICAgICBmaWx0ZXIodmFyaWFibGUgJT4lIHN0cl9kZXRlY3QoJ2xpbmVhZ2UnKSkgJT4lIHNlbGVjdChMaW5lYWdlLCBzdXJ2aXZhbCwgbXVsdGl2YXJpYWJsZSwgSFIsIEhSc2UsIEhSX2x3ciwgSFJfdXByLCBuX3BhdGllbnRzLCBzdGF0aXN0aWMsIHB2YWx1ZSkKICAgICkKICAgIAogICAgIyBNdWx0aXZhcmlhYmxlIG1vZGVsIHN0cmF0aWZpeWluZyBvbiBpbnN0aXR1dGUgYW5kIHByaW1hcnkgc3VidHlwZQogICAgbW9kX211bHRpX29zIDwtIGNveHBoKFN1cnYob3N0aW1lLCBvc2NlbnNvcikgfiBnZXQobGluZWFnZSkgKyBSaXNrX0dyb3VwICsgU2V4ICsgQWdlICsgV0JDLCBidWxrMjA0Nl9waCkKICAgIG1vZF9tdWx0aV9lZnMgPC0gY294cGgoU3VydihlZnN0aW1lLCBlZnNjZW5zb3IpIH4gZ2V0KGxpbmVhZ2UpICsgUmlza19Hcm91cCArIFNleCArIEFnZSArIFdCQywgYnVsazIwNDZfcGgpCiAgICAKICAgICMgQ3JlYXRlIGJhc2VsaW5lIG1vZGVscyB3aXRob3V0IGRldiBzdGF0ZSBhYnVuZGFuY2UgdG8gY29tcGFyZSBhZ2FpbnN0IGJ5IG5lc3RlZCBMUlQKICAgIG1vZF9tdWx0aV9vc18wIDwtIGNveHBoKFN1cnYob3N0aW1lLCBvc2NlbnNvcikgfiBSaXNrX0dyb3VwICsgU2V4ICsgQWdlICsgV0JDLCBidWxrMjA0Nl9waCkKICAgIG1vZF9tdWx0aV9lZnNfMCA8LSBjb3hwaChTdXJ2KGVmc3RpbWUsIGVmc2NlbnNvcikgfiBSaXNrX0dyb3VwICsgU2V4ICsgQWdlICsgV0JDLCBidWxrMjA0Nl9waCkKCiAgICBsaW5lYWdlX3N1cnZpdmFsX3BoIDwtIGxpbmVhZ2Vfc3Vydml2YWxfcGggJT4lIGJpbmRfcm93cygKICAgICAgICBzdW1tYXJ5KG1vZF9tdWx0aV9vcykkY29lZmZpY2llbnRzICU+JSBkYXRhLmZyYW1lKCkgJT4lIGRwbHlyOjpyZW5hbWUoSFIgPSAnZXhwLmNvZWYuJywgSFJzZSA9ICdzZS5jb2VmLicsIHN0YXRpc3RpYyA9ICd6JywgcHZhbHVlID0gJ1ByLi4uei4uJykgJT4lIAogICAgICAgICAgICByb3duYW1lc190b19jb2x1bW4oJ3ZhcmlhYmxlJykgJT4lIG11dGF0ZShMaW5lYWdlID0gbGluZWFnZSwgbXVsdGl2YXJpYWJsZSA9IFRSVUUsIHN1cnZpdmFsID0gJ09TJykgJT4lIAogICAgICAgICAgICBtdXRhdGUoSFJfdXByID0gSFIgKyAxLjk2KkhSc2UsIEhSX2x3ciA9IEhSIC0gMS45NipIUnNlLCAKICAgICAgICAgICAgICAgICAgIG5fcGF0aWVudHMgPSBtb2RfbXVsdGlfb3MkbikgJT4lIAogICAgICAgICAgICBmaWx0ZXIodmFyaWFibGUgJT4lIHN0cl9kZXRlY3QoJ2xpbmVhZ2UnKSkgJT4lIHNlbGVjdChMaW5lYWdlLCBzdXJ2aXZhbCwgbXVsdGl2YXJpYWJsZSwgSFIsIEhSc2UsIEhSX2x3ciwgSFJfdXByLCBuX3BhdGllbnRzLCBzdGF0aXN0aWMsIHB2YWx1ZSkgJT4lIAogICAgICAgICAgICAjIG5lc3RlZCBMUlQ6IGhvdyBtdWNoIHByb2dub3N0aWMgaW5mbyBkb2VzIGRldiBzdGF0ZSBhZGQgYmV5b25kIGJhc2VsaW5lIG1vZGVsPwogICAgICAgICAgICBtdXRhdGUocHZhbHVlX25lc3RlZExSVCA9IGFub3ZhKG1vZF9tdWx0aV9vcywgbW9kX211bHRpX29zXzApWzIsJ1AoPnxDaGl8KSddKQogICAgICAgIAogICAgKSAlPiUgYmluZF9yb3dzKAogICAgICAKICAgICAgICBzdW1tYXJ5KG1vZF9tdWx0aV9lZnMpJGNvZWZmaWNpZW50cyAlPiUgZGF0YS5mcmFtZSgpICU+JSBkcGx5cjo6cmVuYW1lKEhSID0gJ2V4cC5jb2VmLicsIEhSc2UgPSAnc2UuY29lZi4nLCBzdGF0aXN0aWMgPSAneicsIHB2YWx1ZSA9ICdQci4uLnouLicpICU+JSAKICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCd2YXJpYWJsZScpICU+JSBtdXRhdGUoTGluZWFnZSA9IGxpbmVhZ2UsIG11bHRpdmFyaWFibGUgPSBUUlVFLCBzdXJ2aXZhbCA9ICdFRlMnKSAlPiUgCiAgICAgICAgICAgIG11dGF0ZShIUl91cHIgPSBIUiArIDEuOTYqSFJzZSwgSFJfbHdyID0gSFIgLSAxLjk2KkhSc2UsIAogICAgICAgICAgICAgICAgICAgbl9wYXRpZW50cyA9IG1vZF9tdWx0aV9lZnMkbikgJT4lCiAgICAgICAgICAgIGZpbHRlcih2YXJpYWJsZSAlPiUgc3RyX2RldGVjdCgnbGluZWFnZScpKSAlPiUgc2VsZWN0KExpbmVhZ2UsIHN1cnZpdmFsLCBtdWx0aXZhcmlhYmxlLCBIUiwgSFJzZSwgSFJfbHdyLCBIUl91cHIsIG5fcGF0aWVudHMsIHN0YXRpc3RpYywgcHZhbHVlKSAlPiUgCiAgICAgICAgICAgICMgbmVzdGVkIExSVDogaG93IG11Y2ggcHJvZ25vc3RpYyBpbmZvIGRvZXMgZGV2IHN0YXRlIGFkZCBiZXlvbmQgYmFzZWxpbmUgbW9kZWw/CiAgICAgICAgICAgIG11dGF0ZShwdmFsdWVfbmVzdGVkTFJUID0gYW5vdmEobW9kX211bHRpX2VmcywgbW9kX211bHRpX2Vmc18wKVsyLCdQKD58Q2hpfCknXSkKICAgICAgICAKICAgICkKfQoKbGluZWFnZV9zdXJ2aXZhbF9waApgYGAKCgpNYWtlIGZvcmVzdCBwbG90cwoKYGBge3IsIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA2fQpzdXJ2X3BoX3VuaSA8LSBsaW5lYWdlX3N1cnZpdmFsX3BoICU+JSBtdXRhdGUobG9ncHZhbCA9IGlmZWxzZShIUiA+IDEsIC1sb2cxMChwdmFsdWUpLCBsb2cxMChwdmFsdWUpKSkgJT4lIAogICAgbXV0YXRlKEFzc29jaWF0aW9uID0gaWZlbHNlKEhSX2x3ciA+IDEgJiBwdmFsdWUgPCAwLjA1LCAnQWR2ZXJzZScsIGlmZWxzZShIUl91cHIgPCAxICYgcHZhbHVlIDwgMC4wNSwgJ0Zhdm9yYWJsZScsICdOLlMuJykpKSAlPiUKICAgIGZpbHRlcihtdWx0aXZhcmlhYmxlID09IEZBTFNFKSAlPiUgCiAgICBtdXRhdGUoc3Vydm5hbWUgPSBpZmVsc2Uoc3Vydml2YWwgPT0gJ09TJywgJ092ZXJhbGwgU3Vydml2YWwnLCBpZmVsc2Uoc3Vydml2YWwgPT0gJ0VGUycsICdFdmVudC1GcmVlIFN1cnZpdmFsJywgJ05BJykpICU+JSAKICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnT3ZlcmFsbCBTdXJ2aXZhbCcsICdFdmVudC1GcmVlIFN1cnZpdmFsJykpKSAlPiUKICAgIG11dGF0ZShMaW5lYWdlID0gaWZlbHNlKExpbmVhZ2UgJWluJSBjKCdNeWVsb2lkX1Byb2cnLCAnRWFybHlfTHltcGhvaWQnLCAnTWF0dXJlX0InLCAnTXVsdGlwb3RlbmN5X1Njb3JlJyksIExpbmVhZ2UgJT4lIHN0cl9yZXBsYWNlKCdfJywgJyAnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBMaW5lYWdlICU+JSBzdHJfcmVwbGFjZSgnX01QUCcsICcvTVBQJykgJT4lIHN0cl9yZXBsYWNlKCdfTksnLCAnL05LJykgJT4lIHN0cl9yZXBsYWNlKCdfJywgJy0nKSkgJT4lIAogICAgICAgICAgICAgZmFjdG9yKGxldmVscyA9IGMoJ011bHRpcG90ZW5jeSBTY29yZScsICdIU0MvTVBQJywgJ015ZWxvaWQgUHJvZycsICdQcmUtcERDJywgJ0Vhcmx5IEx5bXBob2lkJywgJ1Byby1CJywgJ1ByZS1CJykpKSAlPiUgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIydNYXR1cmUgQicsICdUL05LJywgJ01vbm9jeXRlJywgJ0VyeXRocm9pZCcsICdNdWx0aXBvdGVuY3kgU2NvcmUnKSkpICU+JSAKICAgIGdncGxvdChhZXMoeCA9IExpbmVhZ2UsIHkgPSBIUiwgeW1heCA9IEhSX3VwciwgeW1pbiA9IEhSX2x3ciwgY29sb3IgPSBBc3NvY2lhdGlvbikpICsgCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsdHkgPSAyLCBzaXplID0gMC41LCBhbHBoYSA9IDEsIGNvbG9yID0gJ2RhcmtncmV5JykgKyAKICAgIGdlb21fcG9pbnRyYW5nZShzaXplPTAuOCwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9YygwKSkpICsKICAgIGZhY2V0X3dyYXAoLn5zdXJ2bmFtZSwgbmNvbD0xLCBzY2FsZXMgPSAnZnJlZV94JykgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnaW5kaWFucmVkNCcsICcjNjY2NjY2JykpICsgICAKICAgIHRoZW1lX3B1YnIobGVnZW5kID0gJ3JpZ2h0JykgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2NhbGVzOjpwcmV0dHlfYnJlYWtzKG49OCksIGxpbWl0cyA9IGMoMCwyLjUpKSArIHRoZW1lKGF4aXMudGl0bGUueD0gZWxlbWVudF9ibGFuaygpKSArIAogICAgeWxhYignVW5pdmFyaWFibGUgSGF6YXJkIFJhdGlvICAocGVyIDFTRCBpbmNyZWFzZSBpbiBhYnVuZGFuY2UpJykgKyAKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMC41LCBhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTEpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEwLjUpLAogICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLjUpKSArIAogICAgZ2d0aXRsZSgnVW5pdmFyaWFibGUgU3Vydml2YWwgKG4gPSA3OSknKQogICAgCnN1cnZfcGhfbXVsdGkgPC0gbGluZWFnZV9zdXJ2aXZhbF9waCAlPiUgbXV0YXRlKGxvZ3B2YWwgPSBpZmVsc2UoSFIgPiAxLCAtbG9nMTAocHZhbHVlKSwgbG9nMTAocHZhbHVlKSkpICU+JSAKICAgIG11dGF0ZShBc3NvY2lhdGlvbiA9IGlmZWxzZShIUl9sd3IgPiAxICYgcHZhbHVlX25lc3RlZExSVCA8IDAuMDUsICdBZHZlcnNlJywgaWZlbHNlKEhSX3VwciA8IDEgJiBwdmFsdWVfbmVzdGVkTFJUIDwgMC4wNSwgJ0Zhdm9yYWJsZScsICdOLlMuJykpKSAlPiUKICAgIGZpbHRlcihtdWx0aXZhcmlhYmxlID09IFRSVUUpICU+JSAKICAgIG11dGF0ZShzdXJ2bmFtZSA9IGlmZWxzZShzdXJ2aXZhbCA9PSAnT1MnLCAnT3ZlcmFsbCBTdXJ2aXZhbCcsIGlmZWxzZShzdXJ2aXZhbCA9PSAnRUZTJywgJ0V2ZW50LUZyZWUgU3Vydml2YWwnLCAnTkEnKSkgJT4lIAogICAgICAgICAgIGZhY3RvcihsZXZlbHMgPSBjKCdPdmVyYWxsIFN1cnZpdmFsJywgJ0V2ZW50LUZyZWUgU3Vydml2YWwnKSkpICU+JQogICAgbXV0YXRlKExpbmVhZ2UgPSBpZmVsc2UoTGluZWFnZSAlaW4lIGMoJ015ZWxvaWRfUHJvZycsICdFYXJseV9MeW1waG9pZCcsICdNYXR1cmVfQicsICdNdWx0aXBvdGVuY3lfU2NvcmUnKSwgTGluZWFnZSAlPiUgc3RyX3JlcGxhY2UoJ18nLCAnICcpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIExpbmVhZ2UgJT4lIHN0cl9yZXBsYWNlKCdfTVBQJywgJy9NUFAnKSAlPiUgc3RyX3JlcGxhY2UoJ19OSycsICcvTksnKSAlPiUgc3RyX3JlcGxhY2UoJ18nLCAnLScpKSAlPiUgCiAgICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnTXVsdGlwb3RlbmN5IFNjb3JlJywgJ0hTQy9NUFAnLCAnTXllbG9pZCBQcm9nJywgJ1ByZS1wREMnLCAnRWFybHkgTHltcGhvaWQnLCAnUHJvLUInLCAnUHJlLUInKSkpICU+JSAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjJ01hdHVyZSBCJywgJ1QvTksnLCAnTW9ub2N5dGUnLCAnRXJ5dGhyb2lkJywgJ011bHRpcG90ZW5jeSBTY29yZScpKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gTGluZWFnZSwgeSA9IEhSLCB5bWF4ID0gSFJfdXByLCB5bWluID0gSFJfbHdyLCBjb2xvciA9IEFzc29jaWF0aW9uKSkgKyAKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGx0eSA9IDIsIHNpemUgPSAwLjUsIGFscGhhID0gMSwgY29sb3IgPSAnZGFya2dyZXknKSArIAogICAgZ2VvbV9wb2ludHJhbmdlKHNpemU9MC44LCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD1jKDApKSkgKwogICAgZmFjZXRfd3JhcCgufnN1cnZuYW1lLCBuY29sPTEsIHNjYWxlcyA9ICdmcmVlX3gnKSArIAogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCcjNjY2NjY2JykpICsgICAKICAgIHRoZW1lX3B1YnIobGVnZW5kID0gJ3JpZ2h0JykgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2NhbGVzOjpwcmV0dHlfYnJlYWtzKG49OCksIGxpbWl0cyA9IGMoMCwyLjUpKSArIHRoZW1lKGF4aXMudGl0bGUueD0gZWxlbWVudF9ibGFuaygpKSArIAogICAgeWxhYignTXVsdGl2YXJpYWJsZSBIYXphcmQgUmF0aW8gIChwZXIgMVNEIGluY3JlYXNlIGluIGFidW5kYW5jZSknKSArIAogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLjUsIGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMSksIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTAuNSksCiAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAuNSkpICsgCiAgICBnZ3RpdGxlKCdNdWx0aXZhcmlhYmxlIFN1cnZpdmFsIChuID0gNzYpJykKCmdnc2F2ZShwbG90ID0gc3Vydl9waF91bmksIGZpbGVuYW1lID0gJ0JBTExfc3Vydml2YWxfZmlndXJlc19maW5hbC9TdXJ2aXZhbF9QaF9CQUxMMjA0Nl9Vbml2YXJpYWJsZV9CRGV2T25seV9IYXphcmRSYXRpb3MucGRmJywKICAgICBkZXZpY2UgPSAncGRmJywgaGVpZ2h0ID0gNy41LCB3aWR0aCA9IDUuMikKZ2dzYXZlKHBsb3QgPSBzdXJ2X3BoX211bHRpLCBmaWxlbmFtZSA9ICdCQUxMX3N1cnZpdmFsX2ZpZ3VyZXNfZmluYWwvU3Vydml2YWxfUGhfQkFMTDIwNDZfTXVsdGl2YXJpYWJsZV9CRGV2T25seV9IYXphcmRSYXRpb3MucGRmJywKICAgICBkZXZpY2UgPSAncGRmJywgaGVpZ2h0ID0gNy41LCB3aWR0aCA9IDUuMikKCnN1cnZfcGhfdW5pIHwgc3Vydl9waF9tdWx0aQpgYGAKCgoKIyBLaW0gZXQgYWwgQkNSOjpBQkwxIAoKYGBge3J9CmtpbV9waEFMTF92c3QgPC0gcmVhZF9jc3YoJy4uL3N1YnR5cGVfc3ViY2x1c3Rlci9LaW0yMDIzX1BoX0JBTEwvS2ltMjAyM19QaF9CQUxMX1JOQXNlcV92c3QuY3N2JykKIyBjYWxjdWxhdGUgTk1GIHNjb3JlcyBmcm9tIHZzdC1ub3JtYWxpemVkIGRhdGEKa2ltX3BoQUxMX3ZzdF9EZXZTdGF0ZXNjb3JlcyA8LSBjYWxjdWxhdGVfRGV2U3RhdGVfc2NvcmVzKGtpbV9waEFMTF92c3QgJT4lIGNvbHVtbl90b19yb3duYW1lcygnR2VuZScpICU+JSBkYXRhLm1hdHJpeCgpICwgbW9kZWx3ZWlnaHRzX3dpdGhNdWx0aXBvdGVuY3ksIHNjYWxlID0gVCwgc2FtcGxlSUQgPSAnU2FtcGxlJykKa2ltX3BoQUxMX3ZzdF9EZXZTdGF0ZXNjb3Jlc1sxOjIwLCBdCmBgYAoKYGBge3J9CnBoX0JBTExfY29tYmluZWQgPC0gcmVhZF9jc3YoJy4uL3N1YnR5cGVfc3ViY2x1c3Rlci9LaW0yMDIzX1BoX0JBTEwvS2ltMjAyM19QaF9CQUxMX2Fubm9fY2xlYW5lZC5jc3YnKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IGlmZWxzZShNYW51c2NyaXB0X25hbWUgJT4lIHN0cl9kZXRlY3QoJy1SJyksIHBhc3RlMChKQU1MUiwgJ19ubl9NJyksIHBhc3RlMChKQU1MUiwgJ19ubl9QJykpKSAlPiUgCiAgaW5uZXJfam9pbihraW1fcGhBTExfdnN0X0RldlN0YXRlc2NvcmVzKSAKCnBoX0JBTExfY29tYmluZWQgJT4lIHdyaXRlX2NzdignS2ltMjAyM19QaF9CQUxMX0RldlN0YXRlX1Njb3JlZF9NYXkyMDI0LmNzdicpCnBoX0JBTExfY29tYmluZWQKYGBgCgpGb3JtYXQgZm9yIHN1cnZpdmFsIAoKYGBge3J9CnBoX0JBTExfaW5wdXQgPC0gcGhfQkFMTF9jb21iaW5lZCAlPiUgCiAgIyBleGNsdWRlIGEgZGlhZ25vc2lzIC0gcmVsYXBzZSBwYWlyCiAgZmlsdGVyKFN1cnZpdmFsX2FuYWx5c2lzID09ICd5ZXMnKSAlPiUgZmlsdGVyKEFnZV9hdF9keCA+IDE5KSAKCnBoX0JBTExfaW5wdXQKYGBgCgoKYGBge3J9CmxpbmVhZ2Vfc3Vydml2YWxfUGhfS2ltIDwtIGRhdGEuZnJhbWUoKQpkZXZzdGF0ZXMgPC0gYygnTXVsdGlwb3RlbmN5X1Njb3JlJywgJ0hTQ19NUFAnLCAnTXllbG9pZF9Qcm9nJywgJ1ByZV9wREMnLCAnRWFybHlfTHltcGhvaWQnLCAnUHJvX0InLCAnUHJlX0InKQoKZm9yKGxpbmVhZ2UgaW4gZGV2c3RhdGVzKXsKCiAgICAjIFVuaXZhcmlhYmxlIG1vZGVsIHN0cmF0aWZpeWluZyBvbiBpbnN0aXR1dGUgYW5kIHByaW1hcnkgc3VidHlwZQogICAgbW9kX29zIDwtIGNveHBoKFN1cnYoT1MsIGFsaXZlKSB+IGdldChsaW5lYWdlKSwgcGhfQkFMTF9pbnB1dCkKICAgIG1vZF9yZnMgPC0gY294cGgoU3VydihSRlMsIHJlbGFwc2Vfb3JfZGVhdGgpIH4gZ2V0KGxpbmVhZ2UpLCBwaF9CQUxMX2lucHV0KQoKICAgIGxpbmVhZ2Vfc3Vydml2YWxfUGhfS2ltIDwtIGxpbmVhZ2Vfc3Vydml2YWxfUGhfS2ltICU+JSBiaW5kX3Jvd3MoCiAgICAgICAgc3VtbWFyeShtb2Rfb3MpJGNvZWZmaWNpZW50cyAlPiUgZGF0YS5mcmFtZSgpICU+JSBkcGx5cjo6cmVuYW1lKEhSID0gJ2V4cC5jb2VmLicsIEhSc2UgPSAnc2UuY29lZi4nLCBzdGF0aXN0aWMgPSAneicsIHB2YWx1ZSA9ICdQci4uLnouLicpICU+JSAKICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCd2YXJpYWJsZScpICU+JSBtdXRhdGUoTGluZWFnZSA9IGxpbmVhZ2UsIG11bHRpdmFyaWFibGUgPSBGQUxTRSwgc3Vydml2YWwgPSAnT1MnKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcih2YXJpYWJsZSAlPiUgc3RyX2RldGVjdCgnbGluZWFnZScpKSAlPiUgc2VsZWN0KExpbmVhZ2UsIHN1cnZpdmFsLCBtdWx0aXZhcmlhYmxlLCBIUiwgSFJzZSwgc3RhdGlzdGljLCBwdmFsdWUpCiAgICApICU+JSBiaW5kX3Jvd3MoCiAgICAgICAgc3VtbWFyeShtb2RfcmZzKSRjb2VmZmljaWVudHMgJT4lIGRhdGEuZnJhbWUoKSAlPiUgZHBseXI6OnJlbmFtZShIUiA9ICdleHAuY29lZi4nLCBIUnNlID0gJ3NlLmNvZWYuJywgc3RhdGlzdGljID0gJ3onLCBwdmFsdWUgPSAnUHIuLi56Li4nKSAlPiUgCiAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbigndmFyaWFibGUnKSAlPiUgbXV0YXRlKExpbmVhZ2UgPSBsaW5lYWdlLCBtdWx0aXZhcmlhYmxlID0gRkFMU0UsIHN1cnZpdmFsID0gJ1JGUycpICU+JSAKICAgICAgICAgICAgZmlsdGVyKHZhcmlhYmxlICU+JSBzdHJfZGV0ZWN0KCdsaW5lYWdlJykpICU+JSBzZWxlY3QoTGluZWFnZSwgc3Vydml2YWwsIG11bHRpdmFyaWFibGUsIEhSLCBIUnNlLCBzdGF0aXN0aWMsIHB2YWx1ZSkKICAgICkKICAgICAgICAKICAgICMgTXVsdGl2YXJpYWJsZSBtb2RlbCBzdHJhdGlmaXlpbmcgb24gaW5zdGl0dXRlIGFuZCBwcmltYXJ5IHN1YnR5cGUKICAgIG1vZF9tdWx0aV9vcyA8LSBjb3hwaChTdXJ2KE9TLCBhbGl2ZSkgfiBnZXQobGluZWFnZSkgKyBzZXggKyBBZ2VfYXRfZHggKyBXQkMsIHBoX0JBTExfaW5wdXQpCiAgICBtb2RfbXVsdGlfZWZzIDwtIGNveHBoKFN1cnYoUkZTLCByZWxhcHNlX29yX2RlYXRoKSB+IGdldChsaW5lYWdlKSArIHNleCArIEFnZV9hdF9keCArIFdCQywgcGhfQkFMTF9pbnB1dCkKICAgIAogICAgIyBDcmVhdGUgYmFzZWxpbmUgbW9kZWxzIHdpdGhvdXQgZGV2IHN0YXRlIGFidW5kYW5jZSB0byBjb21wYXJlIGFnYWluc3QgYnkgbmVzdGVkIExSVAogICAgbW9kX211bHRpX29zXzAgPC0gY294cGgoU3VydihPUywgYWxpdmUpIH4gc2V4ICsgQWdlX2F0X2R4ICsgV0JDLCBwaF9CQUxMX2lucHV0KQogICAgbW9kX211bHRpX2Vmc18wIDwtIGNveHBoKFN1cnYoUkZTLCByZWxhcHNlX29yX2RlYXRoKSB+IHNleCArIEFnZV9hdF9keCArIFdCQywgcGhfQkFMTF9pbnB1dCkKCiAgICBsaW5lYWdlX3N1cnZpdmFsX1BoX0tpbSA8LSBsaW5lYWdlX3N1cnZpdmFsX1BoX0tpbSAlPiUgYmluZF9yb3dzKAogICAgICAgIHN1bW1hcnkobW9kX211bHRpX29zKSRjb2VmZmljaWVudHMgJT4lIGRhdGEuZnJhbWUoKSAlPiUgZHBseXI6OnJlbmFtZShIUiA9ICdleHAuY29lZi4nLCBIUnNlID0gJ3NlLmNvZWYuJywgc3RhdGlzdGljID0gJ3onLCBwdmFsdWUgPSAnUHIuLi56Li4nKSAlPiUgCiAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbigndmFyaWFibGUnKSAlPiUgbXV0YXRlKExpbmVhZ2UgPSBsaW5lYWdlLCBtdWx0aXZhcmlhYmxlID0gVFJVRSwgc3Vydml2YWwgPSAnT1MnKSAlPiUgCiAgICAgICAgICAgIG11dGF0ZShIUl91cHIgPSBIUiArIDEuOTYqSFJzZSwgSFJfbHdyID0gSFIgLSAxLjk2KkhSc2UpICU+JSAKICAgICAgICAgICAgZmlsdGVyKHZhcmlhYmxlICU+JSBzdHJfZGV0ZWN0KCdsaW5lYWdlJykpICU+JSBzZWxlY3QoTGluZWFnZSwgc3Vydml2YWwsIG11bHRpdmFyaWFibGUsIEhSLCBIUnNlLCBIUl9sd3IsIEhSX3Vwciwgc3RhdGlzdGljLCBwdmFsdWUpICU+JSAKICAgICAgICAgICAgIyBuZXN0ZWQgTFJUOiBob3cgbXVjaCBwcm9nbm9zdGljIGluZm8gZG9lcyBkZXYgc3RhdGUgYWRkIGJleW9uZCBiYXNlbGluZSBtb2RlbD8KICAgICAgICAgICAgbXV0YXRlKHB2YWx1ZV9uZXN0ZWRMUlQgPSBhbm92YShtb2RfbXVsdGlfb3MsIG1vZF9tdWx0aV9vc18wKVsyLCdQKD58Q2hpfCknXSkKICAgICAgICAKICAgICkgJT4lIGJpbmRfcm93cygKICAgICAgCiAgICAgICAgc3VtbWFyeShtb2RfbXVsdGlfZWZzKSRjb2VmZmljaWVudHMgJT4lIGRhdGEuZnJhbWUoKSAlPiUgZHBseXI6OnJlbmFtZShIUiA9ICdleHAuY29lZi4nLCBIUnNlID0gJ3NlLmNvZWYuJywgc3RhdGlzdGljID0gJ3onLCBwdmFsdWUgPSAnUHIuLi56Li4nKSAlPiUgCiAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbigndmFyaWFibGUnKSAlPiUgbXV0YXRlKExpbmVhZ2UgPSBsaW5lYWdlLCBtdWx0aXZhcmlhYmxlID0gVFJVRSwgc3Vydml2YWwgPSAnUkZTJykgJT4lIAogICAgICAgICAgICBtdXRhdGUoSFJfdXByID0gSFIgKyAxLjk2KkhSc2UsIEhSX2x3ciA9IEhSIC0gMS45NipIUnNlKSAlPiUKICAgICAgICAgICAgZmlsdGVyKHZhcmlhYmxlICU+JSBzdHJfZGV0ZWN0KCdsaW5lYWdlJykpICU+JSBzZWxlY3QoTGluZWFnZSwgc3Vydml2YWwsIG11bHRpdmFyaWFibGUsIEhSLCBIUnNlLCBIUl9sd3IsIEhSX3Vwciwgc3RhdGlzdGljLCBwdmFsdWUpICU+JSAKICAgICAgICAgICAgIyBuZXN0ZWQgTFJUOiBob3cgbXVjaCBwcm9nbm9zdGljIGluZm8gZG9lcyBkZXYgc3RhdGUgYWRkIGJleW9uZCBiYXNlbGluZSBtb2RlbD8KICAgICAgICAgICAgbXV0YXRlKHB2YWx1ZV9uZXN0ZWRMUlQgPSBhbm92YShtb2RfbXVsdGlfZWZzLCBtb2RfbXVsdGlfZWZzXzApWzIsJ1AoPnxDaGl8KSddKQogICAgICAgIAogICAgKQp9CgpsaW5lYWdlX3N1cnZpdmFsX1BoX0tpbQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodCA9IDUsIGZpZy53aWR0aCA9IDN9CmxpbmVhZ2Vfc3Vydml2YWxfUGhfS2ltICU+JSBtdXRhdGUobG9ncHZhbCA9IGlmZWxzZShIUiA+IDEsIC1sb2cxMChwdmFsdWUpLCBsb2cxMChwdmFsdWUpKSkgJT4lIAogICAgbXV0YXRlKEhSX3VwciA9IEhSICsgMS45NipIUnNlLCBIUl9sd3IgPSBIUiAtIDEuOTYqSFJzZSkgJT4lCiAgICBtdXRhdGUoQXNzb2NpYXRpb24gPSBpZmVsc2UoSFJfbHdyID4gMSAmIHB2YWx1ZSA8IDAuMDUsICdBZHZlcnNlJywgaWZlbHNlKEhSX3VwciA8IDEgJiBwdmFsdWUgPCAwLjA1LCAnRmF2b3JhYmxlJywgJ04uUy4nKSkpICU+JQogICAgZmlsdGVyKG11bHRpdmFyaWFibGUgPT0gRkFMU0UpICU+JSAKICAgIG11dGF0ZShzdXJ2bmFtZSA9IGlmZWxzZShzdXJ2aXZhbCA9PSAnT1MnLCAnT3ZlcmFsbCBTdXJ2aXZhbCcsIGlmZWxzZShzdXJ2aXZhbCA9PSAnUkZTJywgJ1JlbGFwc2UtRnJlZSBTdXJ2aXZhbCcsICdOQScpKSAlPiUgCiAgICAgICAgICAgZmFjdG9yKGxldmVscyA9IGMoJ092ZXJhbGwgU3Vydml2YWwnLCAnUmVsYXBzZS1GcmVlIFN1cnZpdmFsJykpKSAlPiUKICAgIG11dGF0ZShMaW5lYWdlID0gaWZlbHNlKExpbmVhZ2UgJWluJSBjKCdNeWVsb2lkX1Byb2cnLCAnRWFybHlfTHltcGhvaWQnLCAnTWF0dXJlX0InLCAnTXVsdGlwb3RlbmN5X1Njb3JlJyksIExpbmVhZ2UgJT4lIHN0cl9yZXBsYWNlKCdQcm9nJywgJ1Byb2dlbml0b3InKSAlPiUgc3RyX3JlcGxhY2UoJ1BDMV8nLCcnKSAlPiUgc3RyX3JlcGxhY2UoJ18nLCAnICcpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIExpbmVhZ2UgJT4lIHN0cl9yZXBsYWNlKCdfTVBQJywgJy9NUFAnKSAlPiUgc3RyX3JlcGxhY2UoJ19OSycsICcvTksnKSAlPiUgc3RyX3JlcGxhY2UoJ18nLCAnLScpKSAlPiUgCiAgICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnTXVsdGlwb3RlbmN5IFNjb3JlJywgJ0hTQy9NUFAnLCAnTXllbG9pZCBQcm9nZW5pdG9yJywgJ1ByZS1wREMnLCAnRWFybHkgTHltcGhvaWQnLCAnUHJvLUInLCAnUHJlLUInKSkpICU+JSAKICAgIGdncGxvdChhZXMoeCA9IExpbmVhZ2UsIHkgPSBIUiwgeW1heCA9IEhSX3VwciwgeW1pbiA9IEhSX2x3ciwgY29sb3IgPSBBc3NvY2lhdGlvbikpICsgCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsdHkgPSAyLCBzaXplID0gMC41LCBhbHBoYSA9IDEsIGNvbG9yID0gJ2RhcmtncmV5JykgKyAKICAgIGdlb21fcG9pbnRyYW5nZShzaXplPTAuOCwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9YygwKSkpICsKICAgIGZhY2V0X3dyYXAoLn5zdXJ2bmFtZSwgbmNvbD0xLCBzY2FsZXMgPSAnZnJlZV94JykgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnaW5kaWFucmVkNCcsICdkYXJrZ3JlZW4nLCAnIzY2NjY2NicpKSArICAgIycjNzQ0RjgwJywgJyMzMDMwMzAnCiAgICB0aGVtZV9wdWJyKGxlZ2VuZCA9ICdyaWdodCcpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNjYWxlczo6cHJldHR5X2JyZWFrcyhuPTgpLCBsaW1pdHMgPSBjKDAsMi41KSkgKyB0aGVtZShheGlzLnRpdGxlLng9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICAgIHlsYWIoJ1VuaXZhcmlhYmxlIEhhemFyZCBSYXRpbyAgKHBlciAxU0QgaW5jcmVhc2UgaW4gYWJ1bmRhbmNlKScpICsgI3lsaW0oYygwLCAyLjUpKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAuNSwgYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTExKSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMC41KSwKICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMC41KSkgKyAKICAgIGdndGl0bGUoJ0JDUjo6QUJMMSBBZHVsdCBCLUFMTCAobiA9IDQxKScpCiAgICAKZ2dzYXZlKGZpbGVuYW1lID0gJ0JBTExfc3Vydml2YWxfZmlndXJlc19maW5hbC9TdXJ2aXZhbF9QaF9LaW0yMDIzX1VuaXZhcmlhYmxlX0JEZXZPbmx5X0hhemFyZFJhdGlvcy5wZGYnLCBkZXZpY2UgPSAncGRmJywgaGVpZ2h0ID0gNy41LCB3aWR0aCA9IDQuNSkKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSAzfQpsaW5lYWdlX3N1cnZpdmFsX1BoX0tpbSAlPiUgbXV0YXRlKGxvZ3B2YWwgPSBpZmVsc2UoSFIgPiAxLCAtbG9nMTAocHZhbHVlKSwgbG9nMTAocHZhbHVlKSkpICU+JSAKICAgIG11dGF0ZShIUl91cHIgPSBIUiArIDEuOTYqSFJzZSwgSFJfbHdyID0gSFIgLSAxLjk2KkhSc2UpICU+JQogICAgbXV0YXRlKEFzc29jaWF0aW9uID0gaWZlbHNlKEhSX2x3ciA+IDEgJiBwdmFsdWVfbmVzdGVkTFJUIDwgMC4wNSwgJ0FkdmVyc2UnLCBpZmVsc2UoSFJfdXByIDwgMSAmIHB2YWx1ZV9uZXN0ZWRMUlQgPCAwLjA1LCAnRmF2b3JhYmxlJywgJ04uUy4nKSkpICU+JQogICAgZmlsdGVyKG11bHRpdmFyaWFibGUgPT0gVFJVRSkgJT4lIAogICAgbXV0YXRlKHN1cnZuYW1lID0gaWZlbHNlKHN1cnZpdmFsID09ICdPUycsICdPdmVyYWxsIFN1cnZpdmFsJywgaWZlbHNlKHN1cnZpdmFsID09ICdSRlMnLCAnUmVsYXBzZS1GcmVlIFN1cnZpdmFsJywgJ05BJykpICU+JSAKICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnT3ZlcmFsbCBTdXJ2aXZhbCcsICdSZWxhcHNlLUZyZWUgU3Vydml2YWwnKSkpICU+JQogICAgbXV0YXRlKExpbmVhZ2UgPSBpZmVsc2UoTGluZWFnZSAlaW4lIGMoJ015ZWxvaWRfUHJvZycsICdFYXJseV9MeW1waG9pZCcsICdNYXR1cmVfQicsICdNdWx0aXBvdGVuY3lfU2NvcmUnKSwgTGluZWFnZSAlPiUgc3RyX3JlcGxhY2UoJ1Byb2cnLCAnUHJvZ2VuaXRvcicpICU+JSBzdHJfcmVwbGFjZSgnUEMxXycsJycpICU+JSBzdHJfcmVwbGFjZSgnXycsICcgJyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgTGluZWFnZSAlPiUgc3RyX3JlcGxhY2UoJ19NUFAnLCAnL01QUCcpICU+JSBzdHJfcmVwbGFjZSgnX05LJywgJy9OSycpICU+JSBzdHJfcmVwbGFjZSgnXycsICctJykpICU+JSAKICAgICAgICAgICAgIGZhY3RvcihsZXZlbHMgPSBjKCdNdWx0aXBvdGVuY3kgU2NvcmUnLCAnSFNDL01QUCcsICdNeWVsb2lkIFByb2dlbml0b3InLCAnUHJlLXBEQycsICdFYXJseSBMeW1waG9pZCcsICdQcm8tQicsICdQcmUtQicpKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gTGluZWFnZSwgeSA9IEhSLCB5bWF4ID0gSFJfdXByLCB5bWluID0gSFJfbHdyLCBjb2xvciA9IEFzc29jaWF0aW9uKSkgKyAKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGx0eSA9IDIsIHNpemUgPSAwLjUsIGFscGhhID0gMSwgY29sb3IgPSAnZGFya2dyZXknKSArIAogICAgZ2VvbV9wb2ludHJhbmdlKHNpemU9MC44LCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD1jKDApKSkgKwogICAgZmFjZXRfd3JhcCgufnN1cnZuYW1lLCBuY29sPTEsIHNjYWxlcyA9ICdmcmVlX3gnKSArIAogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdpbmRpYW5yZWQ0JywgJyM2NjY2NjYnKSkgKyAgICMnIzc0NEY4MCcsICcjMzAzMDMwJwogICAgdGhlbWVfcHVicihsZWdlbmQgPSAncmlnaHQnKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3Mobj04KSwgbGltaXRzID0gYygwLDIuNSkpICsgdGhlbWUoYXhpcy50aXRsZS54PSBlbGVtZW50X2JsYW5rKCkpICsgCiAgICB5bGFiKCdNdWx0aXZhcmlhYmxlIEhhemFyZCBSYXRpbyAgKHBlciAxU0QgaW5jcmVhc2UgaW4gYWJ1bmRhbmNlKScpICsgI3lsaW0oYygwLCAyLjUpKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAuNSwgYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTExKSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMC41KSwKICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMC41KSkgKyAKICAgIGdndGl0bGUoJ0JDUjo6QUJMMSBBZHVsdCBCLUFMTCAobiA9IDQxKScpCiAgICAKZ2dzYXZlKGZpbGVuYW1lID0gJ0JBTExfc3Vydml2YWxfZmlndXJlc19maW5hbC9TdXJ2aXZhbF9QaF9LaW0yMDIzX011bHRpdmFyaWFibGVfQkRldk9ubHlfSGF6YXJkUmF0aW9zLnBkZicsIGRldmljZSA9ICdwZGYnLCBoZWlnaHQgPSA3LjUsIHdpZHRoID0gNC41KQpgYGAKCgojIENvbXBhcmUgd2l0aCBNUEFMCgojIyBTY29yZSBpbiBCLUFMTCB2cyBNUEFMKEIvTSkKCmBgYHtyfQpwYW5sZXVjb2hvcnQgPC0gcmVhZFJEUygiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vT3RoZXIvUGFuTGV1R2VuZUV4cHIvcGFuTGV1VG90YWwtcmxvZy5yZHMiKQpwYW5sZXVjb2hvcnRfYW5ubyA8LSBkYXRhLnRhYmxlOjpmcmVhZCgnLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vT3RoZXIvUGFuTGV1R2VuZUV4cHIvUGFuTGV1Q29ob3J0LmNzdicpIApwYW5sZXVjb2hvcnRfQkFMTF9NUEFMIDwtIHBhbmxldWNvaG9ydFssY29sbmFtZXMocGFubGV1Y29ob3J0KSAlaW4lIGZpbHRlcihwYW5sZXVjb2hvcnRfYW5ubywgZGlzZWFzZSAlaW4lIGMoJ0ItQUxMJywgJ01QQUwnKSkkc2FtcGxlXQpwYW5sZXVjb2hvcnRfQkFMTF9NUEFMICU+JSBkaW0oKQpgYGAKCmBgYHtyfQpwYW5sZXVjb2hvcnRfcmxvZ19EZXZTdGF0ZXNjb3JlcyA8LSBjYWxjdWxhdGVfRGV2U3RhdGVfc2NvcmVzKHBhbmxldWNvaG9ydF9CQUxMX01QQUwsIG1vZGVsd2VpZ2h0c193aXRoTXVsdGlwb3RlbmN5LCBzY2FsZSA9IFQsIHNhbXBsZUlEID0gJ3NhbXBsZScpCnBhbmxldWNvaG9ydF9ybG9nX0RldlN0YXRlc2NvcmVzCmBgYAoKCmBgYHtyfQpwYW5sZXVfc2NvcmVkIDwtIHBhbmxldWNvaG9ydF9ybG9nX0RldlN0YXRlc2NvcmVzICU+JSBsZWZ0X2pvaW4ocGFubGV1Y29ob3J0X2Fubm8pCnBhbmxldV9zY29yZWQgJT4lIHdyaXRlX2NzdigncGFubGV1Y29ob3J0X0JBTExfTVBBTF9ybG9nX0RldlN0YXRlX3Njb3Jlc19NYXkyMDI0LmNzdicpCnBhbmxldV9zY29yZWQKYGBgCgojIyBTYW5pdHkgY2hlY2sgYmV0d2VlbiBkYXRhc2V0cwoKQ2hlY2sgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdHdvIGRhdGFzZXRzIC0gc2FtZSBzYW1wbGVzIGJ1dCBwcmVzdW1hYmx5IGRpZmZlcmVudCBhbGlnbm1lbnRzIGFuZCBkaWZmZXJlbnQgbm9ybWFsaXphdGlvbnMuIAoKYGBge3IsIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSAxMn0KcGFubGV1X3Njb3JlZCAlPiUgZmlsdGVyKGRpc2Vhc2UgPT0gJ0ItQUxMJykgJT4lIAogIHNlbGVjdChzYW1wbGUsIEhTQ19NUFAsIE15ZWxvaWRfUHJvZywgUHJlX3BEQywgRWFybHlfTHltcGhvaWQsIFByb19CLCBQcmVfQiwgTXVsdGlwb3RlbmN5X1Njb3JlKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKC1zYW1wbGUsIHZhbHVlc190byA9ICdybG9nJykgJT4lIAogICMgc3RhbmRhcmRpemUgd2l0aGluIEItQUxMCiAgZ3JvdXBfYnkobmFtZSkgJT4lIG11dGF0ZShybG9nID0gKHJsb2cgLSBtZWFuKHJsb2cpKSAvIHNkKHJsb2cpKSAlPiUgCiAgaW5uZXJfam9pbigKICAgIGJ1bGsyMDQ2ICU+JSBzZWxlY3Qoc2FtcGxlID0gU2FtcGxlSURfb2xkLCBIU0NfTVBQLCBNeWVsb2lkX1Byb2csIFByZV9wREMsIEVhcmx5X0x5bXBob2lkLCBQcm9fQiwgUHJlX0IsIE11bHRpcG90ZW5jeV9TY29yZSkgJT4lIAogICAgICBwaXZvdF9sb25nZXIoLXNhbXBsZSwgdmFsdWVzX3RvID0gJ3ZzdCcpCiAgKSAlPiUgCiAgbXV0YXRlKG5hbWUgPSBmYWN0b3IobmFtZSwgbGV2ZWxzID0gYygnTXVsdGlwb3RlbmN5X1Njb3JlJywgJ0hTQ19NUFAnLCAnTXllbG9pZF9Qcm9nJywgJ1ByZV9wREMnLCAnRWFybHlfTHltcGhvaWQnLCAnUHJvX0InLCAnUHJlX0InKSkpICU+JQogIGdncGxvdChhZXMoeCA9IHJsb2csIHkgPSB2c3QpKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdD0wKSArIGdlb21fdmxpbmUoeGludGVyY2VwdD0wKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKSArIHN0YXRfY29yKCkgKyAKICBmYWNldF93cmFwKC5+bmFtZSwgbmNvbCA9IDQpICsgeGxhYigncmxvZyBub3JtYWxpemF0aW9uIChNb250ZWZpb3JpIGV0IGFsKScpICsgeWxhYigndnN0IG5vcm1hbGl6YXRpb24gKEItQUxMIDIwNDYgY29ob3J0KScpCmBgYAoKCgojIyBDb21wYXJlIEVhcmx5IHN1YnNldHMgdnMgTVBBTCAKCmBgYHtyfQpwYW5nZW5lbGV1X2NvbXBhcmUgPC0gcGFubGV1X3Njb3JlZCAlPiUKICBtdXRhdGUoY2FzZV9JRCA9IHNhbXBsZSAlPiUgc3RyX3JlcGxhY2UoJ18uKicsJycpKSAlPiUgCiAgbGVmdF9qb2luKHJlYWRfY3N2KCcuLi9zdWJ0eXBlX3N1YmNsdXN0ZXIvQWxleGFuZGVyX01QQUxfRnVzaW9ucy5jc3YnKSAlPiUKICAgICAgICAgICAgICBtdXRhdGUoWk5GMzg0ciA9IGlmZWxzZSgoZ2VuZV9hID09ICdaTkYzODQnKSB8IChnZW5lX2IgPT0gJ1pORjM4NCcpLCAxLCAwKSwgCiAgICAgICAgICAgICAgICAgICAgIEtNVDJBciA9IGlmZWxzZSgoZ2VuZV9hID09ICdLTVQyQScpIHwgKGdlbmVfYiA9PSAnS01UMkEnKSwgMSwgMCkpICU+JSAKICAgICAgICAgICAgICBncm91cF9ieShjYXNlX0lEKSAlPiUgCiAgICAgICAgICAgICAgc3VtbWFyaXNlKFpORjM4NHIgPSBpZmVsc2Uoc3VtKFpORjM4NHIpID49IDEsICdNUEFMKFpORjM4NHIpJywgJ090aGVyJyksIAogICAgICAgICAgICAgICAgICAgICAgICBLTVQyQXIgPSBpZmVsc2Uoc3VtKEtNVDJBcikgPj0gMSwgJ0tNVDJBcicsICdPdGhlcicpKSAlPiUgdW5pcXVlKCkgKSAlPiUgCiAgbXV0YXRlKGRpc2Vhc2Vfc3VidHlwZSA9IGlmZWxzZShpcy5uYShaTkYzODRyKSwgc3VidHlwZSwgaWZlbHNlKFpORjM4NHIgPT0gJ01QQUwoWk5GMzg0ciknLCBaTkYzODRyLCBzdWJ0eXBlKSkpICU+JSAKICBsZWZ0X2pvaW4oIAogICAgYmluZF9yb3dzKAogICAgICByZWFkX2NzdignLi4vc3VidHlwZV9zdWJjbHVzdGVyL0tNVDJBX3N1YmNsdXN0ZXIxNDIuY3N2JykgJT4lIG11dGF0ZShjYXNlX0lEID0gUGF0aWVudCAlPiUgc3RyX3JlcGxhY2UoJ18uKicsJycpKSAlPiUgc2VsZWN0KGNhc2VfSUQsIHN1YnNldCA9IFN1Ymdyb3VwKSwKICAgICAgcmVhZF90c3YoJy4uL3N1YnR5cGVfc3ViY2x1c3Rlci9EVVg0X2J1bGsudHh0JywgY29sX25hbWVzPUYpICU+JSBzZWxlY3QoY2FzZV9JRCA9IFgxLCBzdWJzZXQgPSBYMikpCiAgICApICU+JSAKICBsZWZ0X2pvaW4oCiAgICByZWFkX3RzdignLi4vc3Vydml2YWxfYW5hbHlzaXNfb2xkL1BoX0JBTExfQ2VsbG9mT3JpZ2luX1N1YnR5cGUudHN2JykgJT4lIHNlbGVjdChzYW1wbGUgPSBTYW1wbGVzLCBDbHVzdGVyKQogICkgJT4lIAogIG11dGF0ZShzdWJzZXQgPSBpZmVsc2UoaXMubmEoc3Vic2V0KSwgQ2x1c3Rlciwgc3Vic2V0KSkKCnBhbmdlbmVsZXVfY29tcGFyZSAlPiUgd3JpdGVfY3N2KCJwYW5nZW5lbGV1X2NvbXBhcmVfQkFMTF9NUEFMX0RldlN0YXRlX01heTIwMjQuY3N2IikKcGFuZ2VuZWxldV9jb21wYXJlJHN1YnNldCAlPiUgdGFibGUoKQpgYGAKCgpgYGB7cn0KcGFuZ2VuZWxldV9jb21wYXJlIDwtIHJlYWRfY3N2KCJwYW5nZW5lbGV1X2NvbXBhcmVfQkFMTF9NUEFMX0RldlN0YXRlX01heTIwMjQuY3N2IikKcGFuZ2VuZWxldV9jb21wYXJlICU+JSBzZWxlY3QoZGlzZWFzZV9zdWJ0eXBlLCBzdWJzZXQpICU+JSB0YWJsZSgpCmBgYAoKCmBgYHtyfQojIFN1YnNldCB0byBpbmNsdWRlIEItQUxMIGFuZCBCLWxpbmVhZ2UgTVBBTHMKcGFuZ2VuZWxldV9jb21wYXJlIDwtIHBhbmdlbmVsZXVfY29tcGFyZSAlPiUgZmlsdGVyKGRpc2Vhc2UyICVpbiUgYygnQi1BTEwnLCAnTVBBTChBVUwpJywgJ01QQUwoQi9NKScsICdNUEFMKEtNVDJBciknLCAnTVBBTChQaCknKSkgCmFidW5kYW5jZXMgPC0gYygnTXVsdGlwb3RlbmN5X1Njb3JlJywgJ0hTQ19NUFAnLCAnRWFybHlfTHltcGhvaWQnLCAnTXllbG9pZF9Qcm9nJywgJ1ByZV9wREMnLCAnUHJvX0InLCAnUHJlX0InKQoKCiMgR2V0IGNhdGVnb3JpZXMKTVBBTF9CQUxMX2NhdGVnb3JpZXMgPC0gYmluZF9yb3dzKAogICMgWk5GMzg0CiAgcGFuZ2VuZWxldV9jb21wYXJlICU+JSBmaWx0ZXIoZGlzZWFzZV9zdWJ0eXBlICU+JSBzdHJfZGV0ZWN0KCdaTkYzODQnKSkgJT4lIAogICAgbXV0YXRlKGRpc2Vhc2VfY2F0ZWdvcnkgPSBwYXN0ZTAoJ1pORjM4NCAnLCBkaXNlYXNlKSkgJT4lIAogICAgc2VsZWN0KGRpc2Vhc2VfY2F0ZWdvcnksIGFidW5kYW5jZXMsIGRpc2Vhc2UsIGRpc2Vhc2UyLCBzdWJzZXQpLCAKICAjIEJDUjo6QUJMMQogIHBhbmdlbmVsZXVfY29tcGFyZSAlPiUgZmlsdGVyKGRpc2Vhc2Vfc3VidHlwZSAlPiUgc3RyX2RldGVjdCgnUGgnKSkgJT4lIAogICAgbXV0YXRlKGRpc2Vhc2VfY2F0ZWdvcnkgPSBwYXN0ZTAoJ0JDUjo6QUJMMSAnLCBpZmVsc2UoZGlzZWFzZSA9PSAnTVBBTCcsICdNUEFMJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN1YnNldCAlPiUgc3RyX2RldGVjdCgnRWFybHknKSwgJ0Vhcmx5LVBybycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN1YnNldCAlPiUgc3RyX2RldGVjdCgnSW50ZXInKSwgJ0ludGVyLVBybycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN1YnNldCAlPiUgc3RyX2RldGVjdCgnTGF0ZScpLCAnTGF0ZS1Qcm8nLCAnTkEnKSkpKSkpICU+JSAKICAgIHNlbGVjdChkaXNlYXNlX2NhdGVnb3J5LCBhYnVuZGFuY2VzLCBkaXNlYXNlLCBkaXNlYXNlMiwgc3Vic2V0KSAlPiUgZmlsdGVyKGRpc2Vhc2VfY2F0ZWdvcnkgIT0gJ0JDUjo6QUJMMSBOQScpLCAKICAjIEtNVDJBCiAgcGFuZ2VuZWxldV9jb21wYXJlICU+JSBmaWx0ZXIoZGlzZWFzZV9zdWJ0eXBlICU+JSBzdHJfZGV0ZWN0KCdLTVQyQScpKSAlPiUgCiAgICBtdXRhdGUoZGlzZWFzZV9jYXRlZ29yeSA9IHBhc3RlMCgnS01UMkEgJywgaWZlbHNlKGRpc2Vhc2UgPT0gJ01QQUwnLCAnTVBBTCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzdWJzZXQgJT4lIHN0cl9kZXRlY3QoJ0tNVDJBLWInKSwgJ0Vhcmx5JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc3Vic2V0ICU+JSBzdHJfZGV0ZWN0KCdLTVQyQS1hJyksICdDb21taXR0ZWQnLCAnTkEnKSkpKSkgJT4lIAogICAgc2VsZWN0KGRpc2Vhc2VfY2F0ZWdvcnksIGFidW5kYW5jZXMsIGRpc2Vhc2UsIGRpc2Vhc2UyLCBzdWJzZXQpICU+JSBmaWx0ZXIoZGlzZWFzZV9jYXRlZ29yeSAhPSAnS01UMkEgTkEnKSwgCiAgIyBEVVg0CiAgcGFuZ2VuZWxldV9jb21wYXJlICU+JSBmaWx0ZXIoZGlzZWFzZV9zdWJ0eXBlICU+JSBzdHJfZGV0ZWN0KCdEVVg0JykpICU+JSAKICAgIG11dGF0ZShkaXNlYXNlX2NhdGVnb3J5ID0gaWZlbHNlKHN1YnNldCA9PSAnRDEnLCAnRFVYNCBDb21taXR0ZWQnLCBpZmVsc2Uoc3Vic2V0ID09ICdEMicsICdEVVg0IEVhcmx5JywgJ05BJykpKSAlPiUgCiAgICBzZWxlY3QoZGlzZWFzZV9jYXRlZ29yeSwgYWJ1bmRhbmNlcywgZGlzZWFzZSwgZGlzZWFzZTIpICU+JSBmaWx0ZXIoZGlzZWFzZV9jYXRlZ29yeSAhPSAnTkEnKSwgCiAgIyBPdGhlciAKICBwYW5nZW5lbGV1X2NvbXBhcmUgJT4lIGZpbHRlcighZGlzZWFzZV9zdWJ0eXBlICU+JSBzdHJfZGV0ZWN0KCdaTkYzODR8UGh8S01UMkF8RFVYNCcpKSAlPiUKICAgIG11dGF0ZShkaXNlYXNlX2NhdGVnb3J5ID0gcGFzdGUwKCdPdGhlciAnLCBkaXNlYXNlKSkgJT4lIAogICAgc2VsZWN0KGRpc2Vhc2VfY2F0ZWdvcnksIGFidW5kYW5jZXMsIGRpc2Vhc2UsIGRpc2Vhc2UyLCBzdWJzZXQpICU+JSBmaWx0ZXIoZGlzZWFzZV9jYXRlZ29yeSAhPSAnTkEnKSwgCiAgKSAKCk1QQUxfQkFMTF9jYXRlZ29yaWVzICU+JSBzZWxlY3QoZGlzZWFzZV9jYXRlZ29yeSwgZGlzZWFzZSkgJT4lIHRhYmxlKCkKYGBgCgoKYGBge3J9CmNwX211bHRpcG90ZW5jeSA8LSBjdXRwb2ludHIoTVBBTF9CQUxMX2NhdGVnb3JpZXMsIHggPSBNdWx0aXBvdGVuY3lfU2NvcmUsIGNsYXNzID0gZGlzZWFzZSwgbmEucm0gPSBUUlVFLCAKICAgICAgICAgICAgICAgIG1ldGhvZCA9IG1heGltaXplX21ldHJpYywgbWV0cmljID0gc3VtX3NlbnNfc3BlYykKcGxvdChjcF9tdWx0aXBvdGVuY3kpCmBgYAoKYGBge3J9CmN1dG9mZiA8LSBjcF9tdWx0aXBvdGVuY3kkb3B0aW1hbF9jdXRwb2ludAoKcCA8LSBNUEFMX0JBTExfY2F0ZWdvcmllcyAlPiUgCiAgIG11dGF0ZShkaXNlYXNlX2NhdGVnb3J5ID0gZmFjdG9yKGRpc2Vhc2VfY2F0ZWdvcnksIGxldmVscyA9IHJldihjKCdaTkYzODQgTVBBTCcsICdaTkYzODQgQi1BTEwnLCAnS01UMkEgTVBBTCcsICdLTVQyQSBFYXJseScsICdCQ1I6OkFCTDEgTVBBTCcsICdCQ1I6OkFCTDEgRWFybHktUHJvJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnT3RoZXIgTVBBTCcsICdEVVg0IEVhcmx5JywgJ0JDUjo6QUJMMSBJbnRlci1Qcm8nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdCQ1I6OkFCTDEgTGF0ZS1Qcm8nLCAnS01UMkEgQ29tbWl0dGVkJywgJ0RVWDQgQ29tbWl0dGVkJywgJ090aGVyIEItQUxMJykpKSkgJT4lCiAgZ2dwbG90KGFlcyh5ID0gZGlzZWFzZV9jYXRlZ29yeSwgeCA9IE11bHRpcG90ZW5jeV9TY29yZSwgZmlsbD1zdGF0KHgpKSkgKwogICAgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudCgKICAgICAgICBqaXR0ZXJlZF9wb2ludHMgPSBUUlVFLCBzY2FsZSA9IDEuNywKICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3BvaW50c19qaXR0ZXIod2lkdGggPSAwLCBoZWlnaHQgPSAwKSwgCiAgICAgICAgcG9pbnRfc2hhcGUgPSAnfCcsIHBvaW50X3NpemUgPSAzLCBwb2ludF9hbHBoYSA9IDAuMykgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudDIobWlkcG9pbnQ9Y3V0b2ZmLCBoaWdoPScjNzEzMDVEJywgbG93PScjNTA4M0EyJywgbmFtZSA9ICdNdWx0aXBvdGVuY3lcblNjb3JlJykgKwogICAgeGxhYihwYXN0ZTAoJ011bHRpcG90ZW5jeSBTY29yZScpKSArCiAgICB0aGVtZV9wdWJyKCkgKyB4bGltKC0zLCAzLjcpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGN1dG9mZiwgbHR5ID0gMiwgYWxwaGE9MC41KSArIAogICAgeWxhYignJykgKyAKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnLCAKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTIsIGNvbG9yID0gYygnZG9kZ2VyYmx1ZTQnLCAnZG9kZ2VyYmx1ZTQnLCAnZG9kZ2VyYmx1ZTQnLCAnZG9kZ2VyYmx1ZTQnLCAnZG9kZ2VyYmx1ZTQnLCAnZGFya2dyZWVuJywgJ2luZGlhbnJlZDQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnZGFya2dyZWVuJywgJ2luZGlhbnJlZDQnLCAnZGFya2dyZWVuJywgJ2luZGlhbnJlZDQnLCAnZGFya2dyZWVuJywgJ2luZGlhbnJlZDQnKSksCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMykpIApwCmBgYAoKCmBgYHtyfQojIG5lZWQgTVBBTF9CQUxMX2NhdGVnb3JpZXMsIFBoLCBLTVQyQSwgWk5GMzg0CnBsb3RfQkFMTF9NUEFMX2NhdGVnb3JpZXMgPC0gZnVuY3Rpb24odmFsdWUgPSAnTXVsdGlwb3RlbmN5X1Njb3JlJywgdmFsdWVfbmFtZSA9ICdCLUFMTCBNdWx0aXBvdGVuY3kgU2NvcmUnLCBjdXRwb2ludCA9IGN1dHBvaW50LCB5bGltaXRzID0gYygtMywgNCkpewoKICBwMCA8LSBNUEFMX0JBTExfY2F0ZWdvcmllcyAlPiUgCiAgICBmaWx0ZXIoZGlzZWFzZSAlaW4lIGMoJ0ItQUxMJywgJ01QQUwnKSkgJT4lIGZpbHRlcihkaXNlYXNlX2NhdGVnb3J5ICU+JSBzdHJfZGV0ZWN0KCdPdGhlcicpKSAlPiUgCiAgICBtdXRhdGUoY2F0ZWdvcnkgPSAnT3RoZXIgU3VidHlwZXMnKSAlPiUgbXV0YXRlKGRpc2Vhc2VfY2F0ID0gaWZlbHNlKGRpc2Vhc2UgPT0gJ01QQUwnLCAnT3RoZXJcbk1QQUwnLCAnT3RoZXJcbkItQUxMJykgJT4lIGZhY3RvcihsZXZlbHMgPSBjKCdPdGhlclxuTVBBTCcsICdPdGhlclxuQi1BTEwnKSkpICU+JSAKICAgIHNlbGVjdChjYXRlZ29yeSwgZGlzZWFzZV9jYXQsIHZhbHVlKSAlPiUgcGl2b3RfbG9uZ2VyKC1jKGNhdGVnb3J5LCBkaXNlYXNlX2NhdCkpICU+JSAKICAgIGdncGxvdChhZXMoeCA9IGRpc2Vhc2VfY2F0LCB5ID0gdmFsdWUsIGZpbGwgPSBkaXNlYXNlX2NhdCkpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0cG9pbnQsIGx0eSA9IDIsIGFscGhhID0gMC43KSArCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaXplID0gMCwgYWxwaGEgPSAwLjgpICsgZ2diZWVzd2FybTo6Z2VvbV9xdWFzaXJhbmRvbShhZXMoc2l6ZSA9IGRpc2Vhc2VfY2F0KSwgd2lkdGggPSAwLjMpICsgCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCdpbmRpYW5yZWQ0JywgJ2RhcmtncmVlbicpKSArIAogICAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gYygwLjgsIDAuMTUpKSArIAogICAgZmFjZXRfd3JhcCgufmNhdGVnb3J5KSArIAogICAgc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbGlzdChjKCdPdGhlclxuTVBBTCcsICdPdGhlclxuQi1BTEwnKSkpICsgCiAgICB0aGVtZV9wdWJyKGxlZ2VuZCA9ICdub25lJykgKyB5bGFiKHZhbHVlX25hbWUpICsgeGxhYignXG5TdWJncm91cCcpICsgeWxpbSh5bGltaXRzKSArCiAgICB0aGVtZShzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKQogIAogIHAxIDwtIE1QQUxfQkFMTF9jYXRlZ29yaWVzICU+JSBmaWx0ZXIoZGlzZWFzZV9jYXRlZ29yeSAlPiUgc3RyX2RldGVjdCgnQkNSOjpBQkwxJykpICU+JSAKICAgIG11dGF0ZShkaXNlYXNlMyA9IGlmZWxzZShkaXNlYXNlMiA9PSAnQi1BTEwnLCBwYXN0ZTAoJ0JDUjo6QUJMMVxuJywgc3Vic2V0ICU+JSBzdHJfcmVwbGFjZSgnUGhfJywnJykpLCAnQkNSOjpBQkwxXG5NUEFMJykgJT4lICAKICAgICAgICAgICAgIGZhY3RvcihsZXZlbHMgPSByZXYoYygnQkNSOjpBQkwxXG5MYXRlLVBybycsICdCQ1I6OkFCTDFcbkludGVyLVBybycsICdCQ1I6OkFCTDFcbkVhcmx5LVBybycsICdCQ1I6OkFCTDFcbk1QQUwnKSkpKSAlPiUgCiAgICBzZWxlY3QoZGlzZWFzZTMsIHZhbHVlKSAlPiUgI211dGF0ZShkaXNlYXNlMyA9IHBhc3RlMCgnQkNSOjpBQkwxXG4nLGRpc2Vhc2UzKSkgJT4lIAogICAgcGl2b3RfbG9uZ2VyKC1kaXNlYXNlMykgJT4lIAogICAgbXV0YXRlKGNhdGVnb3J5ID0gJ0JDUjo6QUJMMScpICU+JQogICAgbXV0YXRlKGBEaXNlYXNlIFN1Ymdyb3VwYCA9IGlmZWxzZShkaXNlYXNlMyAlPiUgc3RyX2RldGVjdCgnTVBBTCcpLCAnXG5NUEFMIChCL00pXG4nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShkaXNlYXNlMyAlPiUgc3RyX2RldGVjdCgnRWFybHl8Wk5GMzg0JyksICdcbkItQUxMXG5FYXJseS9NdWx0aXBvdGVudFxuJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZGlzZWFzZTMgJT4lIHN0cl9kZXRlY3QoJ0ludGVyJyksICdcbkItQUxMXG5JbnRlclByb1xuJywgJ1xuQi1BTExcbkNvbW1pdHRlZFxuJykpKSAlPiUgCiAgICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnXG5NUEFMIChCL00pXG4nLCAnXG5CLUFMTFxuRWFybHkvTXVsdGlwb3RlbnRcbicsICdcbkItQUxMXG5JbnRlclByb1xuJywgJ1xuQi1BTExcbkNvbW1pdHRlZFxuJykpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBkaXNlYXNlMywgeSA9IHZhbHVlLCBmaWxsID0gYERpc2Vhc2UgU3ViZ3JvdXBgKSkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjdXRwb2ludCwgbHR5ID0gMiwgYWxwaGEgPSAwLjcpICsKICAgIGdlb21fYm94cGxvdChvdXRsaWVyLnNpemUgPSAwLCBhbHBoYSA9IDAuOCkgKyBnZ2JlZXN3YXJtOjpnZW9tX3F1YXNpcmFuZG9tKGFlcyhzaXplID0gYERpc2Vhc2UgU3ViZ3JvdXBgKSwgd2lkdGggPSAwLjMpICsgCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCdpbmRpYW5yZWQ0JywgJyNGRjdGMDAnLCAnIzVFQTJDRicsICcjMTA2MUQ5JykpICsgCiAgICBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXMgPSBjKDEsIDAuNiwgMC43LCAwLjcpKSArIAogICAgZmFjZXRfd3JhcCgufmNhdGVnb3J5KSArIAogICAgc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbGlzdChjKCdCQ1I6OkFCTDFcbk1QQUwnLCAnQkNSOjpBQkwxXG5FYXJseS1Qcm8nKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoJ0JDUjo6QUJMMVxuTVBBTCcsICdCQ1I6OkFCTDFcbkludGVyLVBybycpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCdCQ1I6OkFCTDFcbk1QQUwnLCAnQkNSOjpBQkwxXG5MYXRlLVBybycpKSkgKyAKICAgIHRoZW1lX3B1YnIobGVnZW5kID0gJ25vbmUnKSArIHlsYWIodmFsdWVfbmFtZSkgKyB4bGFiKCdcblN1Ymdyb3VwJykgKyB5bGltKHlsaW1pdHMpICsKICAgIHRoZW1lKHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpLCBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMyksIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpCiAgCiAgcDIgPC0gTVBBTF9CQUxMX2NhdGVnb3JpZXMgJT4lIGZpbHRlcihkaXNlYXNlX2NhdGVnb3J5ICU+JSBzdHJfZGV0ZWN0KCdLTVQyQScpKSAlPiUgCiAgICBtdXRhdGUoZGlzZWFzZTMgPSBpZmVsc2UoZGlzZWFzZSA9PSAnQi1BTEwnLCBzdWJzZXQsIGlmZWxzZShkaXNlYXNlID09ICdNUEFMJywgJ0tNVDJBLXJcbk1QQUwnLCBzdWJzZXQpKSkgJT4lIAogICAgbXV0YXRlKGRpc2Vhc2UzID0gZGlzZWFzZTMgJT4lIHN0cl9yZXBsYWNlKCdLTVQyQS1hJywgJ0tNVDJBLXJcbkNvbW1pdHRlZCcpICU+JSBzdHJfcmVwbGFjZSgnS01UMkEtYicsICdLTVQyQS1yXG5FYXJseScpICU+JSAKICAgICAgICAgICAgIGZhY3RvcihsZXZlbHMgPSBjKCdLTVQyQS1yXG5NUEFMJywgJ0tNVDJBLXJcbkVhcmx5JywgJ0tNVDJBLXJcbkNvbW1pdHRlZCcpKSkgJT4lIGZpbHRlcihkaXNlYXNlMyAhPSAnTkEnKSAlPiUgCiAgICBzZWxlY3QoZGlzZWFzZTMsIHZhbHVlKSAlPiUgI211dGF0ZShkaXNlYXNlMyA9IHBhc3RlMCgnQkNSOjpBQkwxXG4nLGRpc2Vhc2UzKSkgJT4lIAogICAgcGl2b3RfbG9uZ2VyKC1kaXNlYXNlMykgJT4lIAogICAgbXV0YXRlKGNhdGVnb3J5ID0gJ0tNVDJBLXInKSAlPiUKICAgIG11dGF0ZShgRGlzZWFzZSBTdWJncm91cGAgPSBpZmVsc2UoZGlzZWFzZTMgJT4lIHN0cl9kZXRlY3QoJ01QQUwnKSwgJ1xuTVBBTCAoQi9NKVxuJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZGlzZWFzZTMgJT4lIHN0cl9kZXRlY3QoJ0Vhcmx5fFpORjM4NCcpLCAnXG5CLUFMTFxuRWFybHkvTXVsdGlwb3RlbnRcbicsICdcbkItQUxMXG5Db21taXR0ZWRcbicpKSAlPiUgCiAgICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnXG5NUEFMIChCL00pXG4nLCAnXG5CLUFMTFxuRWFybHkvTXVsdGlwb3RlbnRcbicsICdcbkItQUxMXG5Db21taXR0ZWRcbicpKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gZGlzZWFzZTMsIHkgPSB2YWx1ZSwgZmlsbCA9IGBEaXNlYXNlIFN1Ymdyb3VwYCkpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0cG9pbnQsIGx0eSA9IDIsIGFscGhhID0gMC43KSArCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaXplID0gMCwgYWxwaGEgPSAwLjgpICsgZ2diZWVzd2FybTo6Z2VvbV9xdWFzaXJhbmRvbShhZXMoc2l6ZSA9IGBEaXNlYXNlIFN1Ymdyb3VwYCksIHdpZHRoID0gMC4zKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygnaW5kaWFucmVkNCcsICcjQjIyMTIyJywgJyMxRDkwRkYnKSkgKyAKICAgIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IGMoMSwgMC42LCAwLjgpKSArIAogICAgZmFjZXRfd3JhcCgufmNhdGVnb3J5KSArIAogICAgc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbGlzdChjKCdLTVQyQS1yXG5NUEFMJywgJ0tNVDJBLXJcbkVhcmx5JyksIGMoJ0tNVDJBLXJcbk1QQUwnLCAnS01UMkEtclxuQ29tbWl0dGVkJykpKSArCiAgICB0aGVtZV9wdWJyKGxlZ2VuZCA9ICdub25lJykgKyB5bGFiKHZhbHVlX25hbWUpICsgeGxhYignXG5TdWJncm91cCcpICsgeWxpbSh5bGltaXRzKSArCiAgICB0aGVtZShzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKQogIAogIAogIHAzIDwtIE1QQUxfQkFMTF9jYXRlZ29yaWVzICU+JSBmaWx0ZXIoZGlzZWFzZV9jYXRlZ29yeSAlPiUgc3RyX2RldGVjdCgnWk5GMzg0JykpICU+JSAKICAgIG11dGF0ZShkaXNlYXNlMyA9IHBhc3RlMCgnWk5GMzg0LXJcbicsZGlzZWFzZSkgJT4lIGZhY3RvcihsZXZlbHMgPSBjKCdaTkYzODQtclxuTVBBTCcsICdaTkYzODQtclxuQi1BTEwnKSkpICU+JSBzZWxlY3QoZGlzZWFzZTMsIHZhbHVlKSAlPiUgCiAgICBwaXZvdF9sb25nZXIoLWRpc2Vhc2UzKSAlPiUgCiAgICBtdXRhdGUoY2F0ZWdvcnkgPSAnWk5GMzg0LXInKSAlPiUKICAgIG11dGF0ZShgRGlzZWFzZSBTdWJncm91cGAgPSBpZmVsc2UoZGlzZWFzZTMgJT4lIHN0cl9kZXRlY3QoJ01QQUwnKSwgJ1xuTVBBTCAoQi9NKVxuJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZGlzZWFzZTMgJT4lIHN0cl9kZXRlY3QoJ0Vhcmx5fFpORjM4NCcpLCAnXG5CLUFMTFxuRWFybHkvTXVsdGlwb3RlbnRcbicsICdcbkItQUxMXG5Db21taXR0ZWRcbicpKSAlPiUgCiAgICAgICAgICAgICBmYWN0b3IobGV2ZWxzID0gYygnXG5NUEFMIChCL00pXG4nLCAnXG5CLUFMTFxuRWFybHkvTXVsdGlwb3RlbnRcbicsICdcbkItQUxMXG5Db21taXR0ZWRcbicpKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gZGlzZWFzZTMsIHkgPSB2YWx1ZSwgZmlsbCA9IGBEaXNlYXNlIFN1Ymdyb3VwYCkpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0cG9pbnQsIGx0eSA9IDIsIGFscGhhID0gMC43KSArCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaXplID0gMCwgYWxwaGEgPSAwLjgpICsgZ2diZWVzd2FybTo6Z2VvbV9xdWFzaXJhbmRvbShhZXMoc2l6ZSA9IGBEaXNlYXNlIFN1Ymdyb3VwYCksIHdpZHRoID0gMC4zKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygnaW5kaWFucmVkNCcsICdkb2RnZXJibHVlNCcpKSArIAogICAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gYygxLCAwLjcpKSArIAogICAgZmFjZXRfd3JhcCgufmNhdGVnb3J5KSArIAogICAgc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbGlzdChjKCdaTkYzODQtclxuTVBBTCcsICdaTkYzODQtclxuQi1BTEwnKSkpICsgCiAgICB0aGVtZV9wdWJyKGxlZ2VuZCA9ICdub25lJykgKyB5bGFiKHZhbHVlX25hbWUpICsgeGxhYignXG5TdWJncm91cCcpICsgeWxpbSh5bGltaXRzKSArCiAgICB0aGVtZShzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKQogIAogIHJldHVybihnZ2FycmFuZ2UocDAsIHAxLCBwMiwgcDMsIG5jb2wgPSA0LCB3aWR0aHMgPSBjKDAuNTgsIDEsIDAuOCwgMC41OCkpKQp9CgpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD0zLjIsIGZpZy53aWR0aD0xMH0KcGxvdF9CQUxMX01QQUxfY2F0ZWdvcmllcyh2YWx1ZSA9ICdNdWx0aXBvdGVuY3lfU2NvcmUnLCB2YWx1ZV9uYW1lID0gJ0ItQUxMIE11bHRpcG90ZW5jeSBTY29yZScsIGN1dHBvaW50ID0gY3BfbXVsdGlwb3RlbmN5JG9wdGltYWxfY3V0cG9pbnQsIHlsaW1pdHMgPSBjKC0zLCAzLjc1KSkKZ2dzYXZlKCdCQUxMX011bHRpcG90ZW5jeVNjb3JlX0ZpZ3VyZXMvQkFMTF92c19NUEFMX2J5U3VidHlwZV9NdWx0aXBvdGVuY3lTY29yZS5wZGYnLCBoZWlnaHQgPSA0LjgsIHdpZHRoID0gMTUsIGRldmljZSA9ICdwZGYnKQoKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9My4yLCBmaWcud2lkdGg9MTB9CmNwIDwtIGN1dHBvaW50cihNUEFMX0JBTExfY2F0ZWdvcmllcywgeCA9IEhTQ19NUFAsIGNsYXNzID0gZGlzZWFzZSwgbmEucm0gPSBUUlVFLCAKICAgICAgICAgICAgICAgIG1ldGhvZCA9IG1heGltaXplX21ldHJpYywgbWV0cmljID0gc3VtX3NlbnNfc3BlYykKcGxvdF9CQUxMX01QQUxfY2F0ZWdvcmllcyh2YWx1ZSA9ICdIU0NfTVBQJywgdmFsdWVfbmFtZSA9ICdIU0MvTVBQIEFidW5kYW5jZScsIGN1dHBvaW50ID0gY3Akb3B0aW1hbF9jdXRwb2ludCwgeWxpbWl0cyA9IGMoLTIuOCwgNCkpCmdnc2F2ZSgnQkFMTF9NdWx0aXBvdGVuY3lTY29yZV9GaWd1cmVzL0JBTExfdnNfTVBBTF9ieVN1YnR5cGVfSFNDTVBQLnBkZicsIGhlaWdodCA9IDQuOCwgd2lkdGggPSAxNSwgZGV2aWNlID0gJ3BkZicpCgpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD0zLjIsIGZpZy53aWR0aD0xMH0KY3AgPC0gY3V0cG9pbnRyKE1QQUxfQkFMTF9jYXRlZ29yaWVzLCB4ID0gRWFybHlfTHltcGhvaWQsIGNsYXNzID0gZGlzZWFzZSwgbmEucm0gPSBUUlVFLCAKICAgICAgICAgICAgICAgIG1ldGhvZCA9IG1heGltaXplX21ldHJpYywgbWV0cmljID0gc3VtX3NlbnNfc3BlYykKcGxvdF9CQUxMX01QQUxfY2F0ZWdvcmllcyh2YWx1ZSA9ICdFYXJseV9MeW1waG9pZCcsIHZhbHVlX25hbWUgPSAnRWFybHkgTHltcGhvaWQgQWJ1bmRhbmNlJywgY3V0cG9pbnQgPSBjcCRvcHRpbWFsX2N1dHBvaW50LCB5bGltaXRzID0gYygtMi44LCA0KSkKZ2dzYXZlKCdCQUxMX011bHRpcG90ZW5jeVNjb3JlX0ZpZ3VyZXMvQkFMTF92c19NUEFMX2J5U3VidHlwZV9FYXJseUx5bXBob2lkLnBkZicsIGhlaWdodCA9IDQuOCwgd2lkdGggPSAxNSwgZGV2aWNlID0gJ3BkZicpCgpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD0zLjIsIGZpZy53aWR0aD0xMH0KY3AgPC0gY3V0cG9pbnRyKE1QQUxfQkFMTF9jYXRlZ29yaWVzLCB4ID0gTXllbG9pZF9Qcm9nLCBjbGFzcyA9IGRpc2Vhc2UsIG5hLnJtID0gVFJVRSwgCiAgICAgICAgICAgICAgICBtZXRob2QgPSBtYXhpbWl6ZV9tZXRyaWMsIG1ldHJpYyA9IHN1bV9zZW5zX3NwZWMpCnBsb3RfQkFMTF9NUEFMX2NhdGVnb3JpZXModmFsdWUgPSAnTXllbG9pZF9Qcm9nJywgdmFsdWVfbmFtZSA9ICdNeWVsb2lkIFByb2dlbml0b3IgQWJ1bmRhbmNlJywgY3V0cG9pbnQgPSBjcCRvcHRpbWFsX2N1dHBvaW50LCB5bGltaXRzID0gYygtMi44LCA1KSkKZ2dzYXZlKCdCQUxMX011bHRpcG90ZW5jeVNjb3JlX0ZpZ3VyZXMvQkFMTF92c19NUEFMX2J5U3VidHlwZV9NeWVsb2lkUHJvZy5wZGYnLCBoZWlnaHQgPSA0LjgsIHdpZHRoID0gMTUsIGRldmljZSA9ICdwZGYnKQoKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9My4yLCBmaWcud2lkdGg9MTB9CmNwIDwtIGN1dHBvaW50cihNUEFMX0JBTExfY2F0ZWdvcmllcywgeCA9IFByZV9wREMsIGNsYXNzID0gZGlzZWFzZSwgbmEucm0gPSBUUlVFLCAKICAgICAgICAgICAgICAgIG1ldGhvZCA9IG1heGltaXplX21ldHJpYywgbWV0cmljID0gc3VtX3NlbnNfc3BlYykKcGxvdF9CQUxMX01QQUxfY2F0ZWdvcmllcyh2YWx1ZSA9ICdQcmVfcERDJywgdmFsdWVfbmFtZSA9ICdQcmUtcERDIEFidW5kYW5jZScsIGN1dHBvaW50ID0gY3Akb3B0aW1hbF9jdXRwb2ludCwgeWxpbWl0cyA9IGMoLTIuOCwgNCkpCmdnc2F2ZSgnQkFMTF9NdWx0aXBvdGVuY3lTY29yZV9GaWd1cmVzL0JBTExfdnNfTVBBTF9ieVN1YnR5cGVfUHJlUERDLnBkZicsIGhlaWdodCA9IDQuOCwgd2lkdGggPSAxNSwgZGV2aWNlID0gJ3BkZicpCgpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD0zLjIsIGZpZy53aWR0aD0xMH0KY3AgPC0gY3V0cG9pbnRyKE1QQUxfQkFMTF9jYXRlZ29yaWVzLCB4ID0gUHJvX0IsIGNsYXNzID0gZGlzZWFzZSwgbmEucm0gPSBUUlVFLCAKICAgICAgICAgICAgICAgIG1ldGhvZCA9IG1heGltaXplX21ldHJpYywgbWV0cmljID0gc3VtX3NlbnNfc3BlYykKcGxvdF9CQUxMX01QQUxfY2F0ZWdvcmllcyh2YWx1ZSA9ICdQcm9fQicsIHZhbHVlX25hbWUgPSAnUHJvLUIgQWJ1bmRhbmNlJywgY3V0cG9pbnQgPSBjcCRvcHRpbWFsX2N1dHBvaW50LCB5bGltaXRzID0gYygtMy41LCAzLjMpKQpnZ3NhdmUoJ0JBTExfTXVsdGlwb3RlbmN5U2NvcmVfRmlndXJlcy9CQUxMX3ZzX01QQUxfYnlTdWJ0eXBlX1Byb0IucGRmJywgaGVpZ2h0ID0gNC44LCB3aWR0aCA9IDE1LCBkZXZpY2UgPSAncGRmJykKCmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTMuMiwgZmlnLndpZHRoPTEwfQpjcCA8LSBjdXRwb2ludHIoTVBBTF9CQUxMX2NhdGVnb3JpZXMsIHggPSBQcmVfQiwgY2xhc3MgPSBkaXNlYXNlLCBuYS5ybSA9IFRSVUUsIAogICAgICAgICAgICAgICAgbWV0aG9kID0gbWF4aW1pemVfbWV0cmljLCBtZXRyaWMgPSBzdW1fc2Vuc19zcGVjKQpwbG90X0JBTExfTVBBTF9jYXRlZ29yaWVzKHZhbHVlID0gJ1ByZV9CJywgdmFsdWVfbmFtZSA9ICdQcmUtQiBBYnVuZGFuY2UnLCBjdXRwb2ludCA9IGNwJG9wdGltYWxfY3V0cG9pbnQsIHlsaW1pdHMgPSBjKC0zLCAzLjgpKQpnZ3NhdmUoJ0JBTExfTXVsdGlwb3RlbmN5U2NvcmVfRmlndXJlcy9CQUxMX3ZzX01QQUxfYnlTdWJ0eXBlX1Byb0IucGRmJywgaGVpZ2h0ID0gNC44LCB3aWR0aCA9IDE1LCBkZXZpY2UgPSAncGRmJykKCmBgYAoKCgojIFNjb3JlIG9uIFBoYXJtYWNvdHlwZSBEcnVnIFNjcmVlbmluZyBEYXRhICAKCmBgYHtyfQojIHJlcXVpcmUgZ2VuZSBzeW1ib2wgY29sdW1uIHRvIGJlIG5hbWVkICJHZW5lIgpycGttX3RvX2xvZ1RQTSA8LSBmdW5jdGlvbihkYXQpewogICMgY29udmVydCB0byBUUE0KICBkYXRfVFBNIDwtIGRhdCAlPiUgCiAgICBnYXRoZXIoLUdlbmUsIGtleSA9ICJTYW1wbGUiLCB2YWx1ZSA9ICJSUEtNIikgJT4lCiAgICBncm91cF9ieShTYW1wbGUpICU+JSAKICAgIG11dGF0ZShsb2dUUE0gPSBsb2cxcChSUEtNIC8gc3VtKFJQS00pICogMTAwMDAwMCkpICU+JSAKICAgIHNlbGVjdCgtUlBLTSkgJT4lIHVuZ3JvdXAoKSAlPiUgCiAgICBzcHJlYWQoU2FtcGxlLCBsb2dUUE0pCiAgCiAgcmV0dXJuKGRhdF9UUE0pCn0KCiMgbG9hZCBwaGFybWFjb3R5cGUgZGF0YSBhbmQgY29udmVydCB0byBsb2dUUE0KcGhhcm1hY290eXBlX2Zwa20gPC0gZGF0YS50YWJsZTo6ZnJlYWQoJy4uL3BoYXJtYWNvdHlwZXMvcGhhcm1hY290eXBpbmdfcGVkX3JuYXNlcV9mcGttX0FMTGlkc18wODIzLmNzdicpICU+JSBzZWxlY3QoLUdlbmVJRCkgJT4lIGRwbHlyOjpyZW5hbWUoR2VuZSA9IEdlbmVOYW1lKQpwaGFybWFjb3R5cGVfbG9nVFBNIDwtIHBoYXJtYWNvdHlwZV9mcGttICU+JSBycGttX3RvX2xvZ1RQTSgpCnBoYXJtYWNvdHlwZV9sb2dUUE0gPC0gcGhhcm1hY290eXBlX2xvZ1RQTSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCdHZW5lJykgJT4lIGRhdGEubWF0cml4KCkKcGhhcm1hY290eXBlX2xvZ1RQTSAlPiUgZGltKCkKYGBgCgoKYGBge3J9CnBoYXJtYWNvdHlwZV9sb2dUUE1fc2NvcmVkIDwtIGNhbGN1bGF0ZV9EZXZTdGF0ZV9zY29yZXMocGhhcm1hY290eXBlX2xvZ1RQTSwgbW9kZWx3ZWlnaHRzX3dpdGhNdWx0aXBvdGVuY3ksIHNjYWxlID0gVCwgc2FtcGxlSUQgPSAnUGF0aWVudCBJRCcpCnBoYXJtYWNvdHlwZV9sb2dUUE1fc2NvcmVkICU+JSB3cml0ZV9jc3YoJ0FMTF9waGFybWFjb3R5cGVzX2xvZ1RQTV9EZXZTdGF0ZV9zY29yZXNfTWF5MjAyNC5jc3YnKQpwaGFybWFjb3R5cGVfbG9nVFBNX3Njb3JlZApgYGAKCgoKCgojIyBXaGljaCB2ZXJzaW9uIG9mIFBDMSBiZXN0IHNlcGFyYXRlcyBzdWJ0eXBlcyB3aXRoaW4gS01UMkEsIERVWDQsIEJDUjo6QUJMMT8KCmBgYHtyfQpidWxrMjA0Nl9zdWJ0eXBlX3N1YmNsdXN0ZXIgPC0gYnVsazIwNDYgJT4lIGZpbHRlcihuZXdfU3VidHlwZSAlaW4lIGMoJ0JDUjo6QUJMMScsICdLTVQyQScsICdEVVg0JykpICU+JSBzZWxlY3QoUGF0aWVudCwgUGF0aWVudElEID0gUGF0aWVudElEX29sZCwgbmV3X1N1YnR5cGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTXVsdGlwb3RlbmN5X1Njb3JlLCBIU0NfTVBQLCBFYXJseV9MeW1waG9pZCwgUHJvX0IsIFByZV9CLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluc3RpdHV0ZSwgb3NjZW5zb3IsIG9zdGltZSwgZWZzY2Vuc29yLCBlZnN0aW1lKSAlPiUgCiAgbGVmdF9qb2luKCByZWFkX3RzdigiLi4vc3VidHlwZV9zdWJjbHVzdGVyL1BoX3N1Yl9jbHVzdGVyc19TMjA0Ni50eHQiLCBjb2xfbmFtZXMgPSBjKCdQYXRpZW50SUQnLCAnQ2xhc3MnKSkgJT4lIGRwbHlyOjpyZW5hbWUoUGhfQ2xhc3MgPSBDbGFzcyksIGJ5ID0gJ1BhdGllbnRJRCcpICU+JSAKICBsZWZ0X2pvaW4oIHJlYWRfdHN2KCIuLi9zdWJ0eXBlX3N1YmNsdXN0ZXIvRFVYNF9idWxrLnR4dCIsIGNvbF9uYW1lcyA9IGMoJ1BhdGllbnRJRCcsICdDbGFzcycpKSAlPiUgZHBseXI6OnJlbmFtZShEVVg0X0NsYXNzID0gQ2xhc3MpLCBieSA9ICdQYXRpZW50SUQnKSAlPiUKICBsZWZ0X2pvaW4oIHJlYWRfY3N2KCIuLi9zdWJ0eXBlX3N1YmNsdXN0ZXIvS01UMkFfc3ViY2x1c3RlcjE0Mi5jc3YiKSAlPiUgc2VsZWN0KFBhdGllbnQsIEtNVDJBX0NsYXNzID0gU3ViZ3JvdXApLCBieSA9ICdQYXRpZW50JykgJT4lIAogIGFycmFuZ2UobmV3X1N1YnR5cGUpIAoKYnVsazIwNDZfc3VidHlwZV9zdWJjbHVzdGVyJEtNVDJBX0NsYXNzICU+JSB0YWJsZSgpCmBgYAoKCmBgYHtyfQpjdXRwb2ludHIoYnVsazIwNDZfc3VidHlwZV9zdWJjbHVzdGVyJEhTQ19NUFAsIGJ1bGsyMDQ2X3N1YnR5cGVfc3ViY2x1c3RlciRLTVQyQV9DbGFzcywgbmEucm09VFJVRSkKY3V0cG9pbnRyKGJ1bGsyMDQ2X3N1YnR5cGVfc3ViY2x1c3RlciRFYXJseV9MeW1waG9pZCwgYnVsazIwNDZfc3VidHlwZV9zdWJjbHVzdGVyJEtNVDJBX0NsYXNzLCBuYS5ybT1UUlVFKQpjdXRwb2ludHIoYnVsazIwNDZfc3VidHlwZV9zdWJjbHVzdGVyJE11bHRpcG90ZW5jeV9TY29yZSwgYnVsazIwNDZfc3VidHlwZV9zdWJjbHVzdGVyJEtNVDJBX0NsYXNzLCBuYS5ybT1UUlVFKQpgYGAKCmBgYHtyfQpjdXRwb2ludHIoYnVsazIwNDZfc3VidHlwZV9zdWJjbHVzdGVyJEhTQ19NUFAsIGJ1bGsyMDQ2X3N1YnR5cGVfc3ViY2x1c3RlciREVVg0X0NsYXNzLCBuYS5ybT1UUlVFKQpjdXRwb2ludHIoYnVsazIwNDZfc3VidHlwZV9zdWJjbHVzdGVyJEVhcmx5X0x5bXBob2lkLCBidWxrMjA0Nl9zdWJ0eXBlX3N1YmNsdXN0ZXIkRFVYNF9DbGFzcywgbmEucm09VFJVRSkKY3V0cG9pbnRyKGJ1bGsyMDQ2X3N1YnR5cGVfc3ViY2x1c3RlciRNdWx0aXBvdGVuY3lfU2NvcmUsIGJ1bGsyMDQ2X3N1YnR5cGVfc3ViY2x1c3RlciREVVg0X0NsYXNzLCBuYS5ybT1UUlVFKQpgYGAKCmBgYHtyfQp0ZW1wIDwtIGJ1bGsyMDQ2X3N1YnR5cGVfc3ViY2x1c3RlciAlPiUgZmlsdGVyKFBoX0NsYXNzICU+JSBzdHJfZGV0ZWN0KCJFYXJseXxMYXRlIikpCgpjdXRwb2ludHIodGVtcCRIU0NfTVBQLCB0ZW1wJFBoX0NsYXNzLCBuYS5ybT1UUlVFKQpjdXRwb2ludHIodGVtcCRFYXJseV9MeW1waG9pZCwgdGVtcCRQaF9DbGFzcywgbmEucm09VFJVRSkKY3V0cG9pbnRyKHRlbXAkTXVsdGlwb3RlbmN5X1Njb3JlLCB0ZW1wJFBoX0NsYXNzLCBuYS5ybT1UUlVFKQpgYGAKCmBgYHtyfQp0ZW1wIDwtIGJ1bGsyMDQ2X3N1YnR5cGVfc3ViY2x1c3RlciAlPiUgZmlsdGVyKFBoX0NsYXNzICU+JSBzdHJfZGV0ZWN0KCJFYXJseXxJbnRlciIpKQoKY3V0cG9pbnRyKHRlbXAkSFNDX01QUCwgdGVtcCRQaF9DbGFzcywgbmEucm09VFJVRSkKY3V0cG9pbnRyKHRlbXAkRWFybHlfTHltcGhvaWQsIHRlbXAkUGhfQ2xhc3MsIG5hLnJtPVRSVUUpCmN1dHBvaW50cih0ZW1wJE11bHRpcG90ZW5jeV9TY29yZSwgdGVtcCRQaF9DbGFzcywgbmEucm09VFJVRSkKYGBgCgpgYGB7cn0KdGVtcCA8LSBidWxrMjA0Nl9zdWJ0eXBlX3N1YmNsdXN0ZXIgJT4lIGZpbHRlcihQaF9DbGFzcyAlPiUgc3RyX2RldGVjdCgiTGF0ZXxJbnRlciIpKQoKY3V0cG9pbnRyKHRlbXAkSFNDX01QUCwgdGVtcCRQaF9DbGFzcywgbmEucm09VFJVRSkKY3V0cG9pbnRyKHRlbXAkRWFybHlfTHltcGhvaWQsIHRlbXAkUGhfQ2xhc3MsIG5hLnJtPVRSVUUpCmN1dHBvaW50cih0ZW1wJE11bHRpcG90ZW5jeV9TY29yZSwgdGVtcCRQaF9DbGFzcywgbmEucm09VFJVRSkKYGBgCgoKCgoKCgoK